[BACK]Return to crypt-argon2.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libcrypt

Annotation of src/lib/libcrypt/crypt-argon2.c, Revision 1.13

1.9       jhigh       1: /*
                      2:  * Copyright (c) 2009 The NetBSD Foundation, Inc.
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  *
                     14:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     15:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     16:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     17:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     18:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     19:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     20:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     21:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     22:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     23:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     24:  * POSSIBILITY OF SUCH DAMAGE.
                     25:  */
                     26:
1.11      nia        27: #include <sys/resource.h>
                     28: #include <sys/param.h>
                     29: #include <sys/sysctl.h>
                     30: #include <sys/syslimits.h>
                     31:
1.1       jhigh      32: #include <stdlib.h>
                     33: #include <stdio.h>
                     34: #include <unistd.h>
                     35: #include <stdio.h>
                     36: #include <string.h>
                     37: #include <time.h>
                     38: #include <pwd.h>
                     39: #include <errno.h>
                     40: #include <argon2.h>
                     41:
                     42: #include <err.h>
                     43: #include "crypt.h"
                     44:
1.11      nia        45: crypt_private int
                     46: estimate_argon2_params(argon2_type, uint32_t *,
                     47:     uint32_t *, uint32_t *);
                     48:
1.1       jhigh      49: /* defaults pulled from run.c */
                     50: #define HASHLEN                32
                     51: #define T_COST_DEF     3
                     52: #define LOG_M_COST_DEF         12 /* 2^12 = 4 MiB */
                     53: #define LANES_DEF      1
                     54: #define THREADS_DEF    1
                     55: #define OUTLEN_DEF     32
                     56: #define MAX_PASS_LEN   128
                     57:
                     58: #define ARGON2_CONTEXT_INITIALIZER     \
                     59:        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
                     60:        T_COST_DEF, LOG_M_COST_DEF,\
                     61:        LANES_DEF, THREADS_DEF, \
                     62:        ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
                     63:
                     64: #define ARGON2_ARGON2_STR      "argon2"
                     65: #define ARGON2_ARGON2I_STR     "argon2i"
                     66: #define ARGON2_ARGON2D_STR     "argon2d"
                     67: #define ARGON2_ARGON2ID_STR    "argon2id"
                     68:
1.11      nia        69: /*
                     70:  * Unpadded Base64 calculations are taken from the Apache2/CC-0
                     71:  * licensed libargon2 for compatibility
                     72:  */
1.7       nia        73:
                     74: /*
                     75:  * Some macros for constant-time comparisons. These work over values in
                     76:  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
                     77:  */
                     78: #define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
                     79: #define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
                     80: #define GE(x, y) (GT(y, x) ^ 0xFF)
                     81: #define LT(x, y) GT(y, x)
                     82: #define LE(x, y) GE(y, x)
                     83:
                     84: static unsigned
                     85: b64_char_to_byte(int c)
                     86: {
                     87:     unsigned x;
                     88:
                     89:     x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
                     90:         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
                     91:         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
                     92:         (EQ(c, '/') & 63);
                     93:     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
                     94: }
                     95:
                     96: static const char *
                     97: from_base64(void *dst, size_t *dst_len, const char *src)
                     98: {
                     99:        size_t len;
                    100:        unsigned char *buf;
                    101:        unsigned acc, acc_len;
                    102:
                    103:        buf = (unsigned char *)dst;
                    104:        len = 0;
                    105:        acc = 0;
                    106:        acc_len = 0;
                    107:        for (;;) {
                    108:                unsigned d;
                    109:
                    110:                d = b64_char_to_byte(*src);
                    111:                if (d == 0xFF) {
                    112:                        break;
                    113:                }
                    114:                src++;
                    115:                acc = (acc << 6) + d;
                    116:                acc_len += 6;
                    117:                if (acc_len >= 8) {
                    118:                        acc_len -= 8;
                    119:                        if ((len++) >= *dst_len) {
                    120:                                return NULL;
                    121:                        }
                    122:                        *buf++ = (acc >> acc_len) & 0xFF;
                    123:                }
                    124:        }
                    125:
                    126:        /*
                    127:         * If the input length is equal to 1 modulo 4 (which is
                    128:         * invalid), then there will remain 6 unprocessed bits;
                    129:         * otherwise, only 0, 2 or 4 bits are buffered. The buffered
                    130:         * bits must also all be zero.
                    131:         */
                    132:        if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
                    133:                return NULL;
                    134:        }
                    135:        *dst_len = len;
                    136:        return src;
                    137: }
                    138:
1.11      nia       139: /*
                    140:  * Used to find default parameters that perform well on the host
                    141:  * machine.  Inputs should dereference to either 0 (to estimate),
                    142:  * or desired value.
                    143:  */
                    144: crypt_private int
                    145: estimate_argon2_params(argon2_type atype, uint32_t *etime,
                    146:     uint32_t *ememory, uint32_t *ethreads)
                    147: {
                    148:        const int mib[] = { CTL_HW, HW_USERMEM64 };
                    149:        struct timespec tp1, tp2, delta;
                    150:        char tmp_salt[16];
                    151:        char tmp_pwd[16];
                    152:        uint32_t tmp_hash[32];
                    153:        char tmp_encoded[256];
                    154:        struct rlimit rlim;
                    155:        uint64_t max_mem;
                    156:        size_t max_mem_sz = sizeof(max_mem);
                    157:        /* low values from argon2 test suite... */
                    158:        uint32_t memory = 256;
1.13    ! nia       159:        uint32_t time = 3;
1.11      nia       160:        uint32_t threads = 1;
                    161:
                    162:        if (*ememory < ARGON2_MIN_MEMORY) {
                    163:                /*
                    164:                 * attempt to find a reasonble bound for memory use
                    165:                 */
                    166:                if (sysctl(mib, __arraycount(mib),
                    167:                    &max_mem, &max_mem_sz, NULL, 0) < 0) {
                    168:                        goto error;
                    169:                }
                    170:                if (getrlimit(RLIMIT_AS, &rlim) < 0)
                    171:                        goto error;
                    172:                if (max_mem > rlim.rlim_cur && rlim.rlim_cur != RLIM_INFINITY)
                    173:                        max_mem = rlim.rlim_cur;
                    174:
                    175:                /*
                    176:                 * Note that adding memory also greatly slows the algorithm.
                    177:                 * Do we need to be concerned about memory usage during
                    178:                 * concurrent connections?
                    179:                 */
                    180:                max_mem /= 1000000;
                    181:                if (max_mem > 30000) {
                    182:                        memory = 8192;
                    183:                } else if (max_mem > 7000) {
                    184:                        memory = 4096;
                    185:                } else if (max_mem > 24) {
                    186:                        memory = 256;
                    187:                } else {
                    188:                        memory = ARGON2_MIN_MEMORY;
                    189:                }
                    190:        } else {
                    191:                memory = *ememory;
                    192:        }
                    193:
                    194:        if (*etime < ARGON2_MIN_TIME) {
                    195:                /*
                    196:                 * just fill these with random stuff since we'll immediately
                    197:                 * discard them after calculating hashes for 1 second
                    198:                 */
                    199:                arc4random_buf(tmp_pwd, sizeof(tmp_pwd));
                    200:                arc4random_buf(tmp_salt, sizeof(tmp_salt));
                    201:
                    202:                if (clock_gettime(CLOCK_MONOTONIC, &tp1) == -1)
                    203:                        goto error;
                    204:                for (; delta.tv_sec < 1 && time < ARGON2_MAX_TIME; ++time) {
                    205:                        if (argon2_hash(time, memory, threads,
                    206:                            tmp_pwd, sizeof(tmp_pwd),
                    207:                            tmp_salt, sizeof(tmp_salt),
                    208:                            tmp_hash, sizeof(tmp_hash),
                    209:                            tmp_encoded, sizeof(tmp_encoded),
                    210:                            atype, ARGON2_VERSION_NUMBER) != ARGON2_OK) {
                    211:                                goto reset;
                    212:                        }
                    213:                        if (clock_gettime(CLOCK_MONOTONIC, &tp2) == -1)
                    214:                                break;
                    215:                        if (timespeccmp(&tp1, &tp2, >))
                    216:                                break; /* broken system... */
                    217:                        timespecsub(&tp2, &tp1, &delta);
                    218:                }
                    219:        } else {
                    220:                time = *etime;
                    221:        }
                    222:
                    223: error:
                    224:        *etime = time;
                    225:        *ememory = memory;
                    226:        *ethreads = threads;
                    227:        return 0;
                    228: reset:
1.13    ! nia       229:        time = 3;
1.11      nia       230:        memory = 256;
                    231:        threads = 1;
                    232:        goto error;
                    233: }
                    234:
                    235:
1.1       jhigh     236: /* process params to argon2 */
                    237: /* we don't force param order as input, */
                    238: /* but we do provide the expected order to argon2 api */
1.7       nia       239: static int
                    240: decode_option(argon2_context *ctx, argon2_type *atype, const char *option)
1.1       jhigh     241: {
1.7       nia       242:        size_t tmp = 0;
                    243:         char *in = 0, *inp;
                    244:         char *a = 0;
                    245:         char *p = 0;
1.1       jhigh     246:        size_t sl;
1.7       nia       247:        int error = 0;
1.1       jhigh     248:
                    249:         in = (char *)strdup(option);
                    250:        inp = in;
                    251:
                    252:        if (*inp == '$') inp++;
                    253:
                    254:        a = strsep(&inp, "$");
                    255:
                    256:        sl = strlen(a);
                    257:
                    258:        if (sl == strlen(ARGON2_ARGON2I_STR) &&
                    259:           !(strcmp(ARGON2_ARGON2I_STR, a))) {
                    260:                *atype=Argon2_i;
                    261:        } else if (sl == strlen(ARGON2_ARGON2D_STR) &&
                    262:                !(strcmp(ARGON2_ARGON2D_STR, a))) {
                    263:                *atype=Argon2_d;
                    264:        }
                    265:        else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
                    266:                !(strcmp(ARGON2_ARGON2ID_STR, a))) {
                    267:                *atype=Argon2_id;
                    268:        } else { /* default to id, we assume simple mistake */
                    269:                /* don't abandon yet */
                    270:                *atype=Argon2_id;
                    271:        }
                    272:
                    273:        a = strsep(&inp, "$");
                    274:
1.3       nia       275:        /* parse the version number of the hash, if it's there */
                    276:        if (strncmp(a, "v=", 2) == 0) {
                    277:                a += 2;
                    278:                if ((getnum(a, &tmp))<0) { /* on error, default to current */
                    279:                        /* should start thinking about aborting */
1.4       nia       280:                        ctx->version = ARGON2_VERSION_10;
1.3       nia       281:                } else {
                    282:                        ctx->version = tmp;
                    283:                }
                    284:                a = strsep(&inp, "$");
                    285:        } else {
                    286:                /*
                    287:                 * This is a parameter list, not a version number, use the
                    288:                 * default version.
                    289:                 */
1.4       nia       290:                ctx->version = ARGON2_VERSION_10;
1.1       jhigh     291:        }
                    292:
                    293:        /* parse labelled argon2 params */
                    294:        /* m_cost (m)
                    295:         * t_cost (t)
                    296:         * threads (p)
                    297:         */
                    298:        while ((p = strsep(&a, ","))) {
                    299:                switch (*p) {
                    300:                        case 'm':
                    301:                                p += strlen("m=");
                    302:                                if ((getnum(p, &tmp)) < 0) {
                    303:                                        --error;
                    304:                                } else {
                    305:                                        ctx->m_cost = tmp;
                    306:                                }
                    307:                                break;
                    308:                        case 't':
                    309:                                p += strlen("t=");
                    310:                                if ((getnum(p, &tmp)) < 0) {
                    311:                                        --error;
                    312:                                } else {
                    313:                                        ctx->t_cost = tmp;
                    314:                                }
                    315:                                break;
                    316:                        case 'p':
                    317:                                p += strlen("p=");
                    318:                                if ((getnum(p, &tmp)) < 0) {
                    319:                                        --error;
                    320:                                } else {
                    321:                                        ctx->threads = tmp;
                    322:                                }
                    323:                                break;
                    324:                        default:
                    325:                                return -1;
                    326:
                    327:                }
                    328:        }
                    329:
                    330:        a = strsep(&inp, "$");
                    331:
1.7       nia       332:        sl = ctx->saltlen;
                    333:
                    334:        if (from_base64(ctx->salt, &sl, a) == NULL)
                    335:                return -1;
                    336:
                    337:        ctx->saltlen = sl;
1.1       jhigh     338:
                    339:        a = strsep(&inp, "$");
                    340:
1.3       nia       341:        if (a) {
                    342:                snprintf((char *)ctx->pwd, ctx->pwdlen, "%s", a);
1.1       jhigh     343:        } else {
                    344:                /* don't care if passwd hash is missing */
                    345:                /* if missing, most likely coming from */
                    346:                /* pwhash or similar */
                    347:        }
                    348:
                    349:        /* free our token buffer */
                    350:         free(in);
                    351:
                    352:        /* 0 on success, <0 otherwise */
                    353:         return error;
                    354: }
                    355:
1.10      nia       356: crypt_private char *
1.1       jhigh     357: __crypt_argon2(const char *pw, const char * salt)
                    358: {
                    359:        /* we use the libargon2 api to generate */
                    360:        /* return code */
1.7       nia       361:        int rc = 0;
1.1       jhigh     362:        /* output buffer */
                    363:        char ebuf[32];
                    364:        /* argon2 variable, default to id */
                    365:        argon2_type atype = Argon2_id;
                    366:        /* default to current argon2 version */
                    367:        /* argon2 context to collect params */
                    368:        argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
                    369:        /* argon2 encoded buffer */
                    370:        char encodebuf[256];
                    371:        /* argon2 salt buffer */
                    372:        char saltbuf[128];
                    373:        /* argon2 pwd buffer */
                    374:        char pwdbuf[128];
                    375:        /* returned static buffer */
                    376:        static char rbuf[512];
                    377:
                    378:        /* clear buffers */
1.6       nia       379:        explicit_memset(rbuf, 0, sizeof(rbuf));
1.1       jhigh     380:
                    381:        /* we use static buffers to avoid allocation */
                    382:        /* and easier cleanup */
                    383:        ctx.out = (uint8_t *)ebuf;
                    384:        ctx.outlen = sizeof(ebuf);
                    385:
                    386:        ctx.out = (uint8_t *)encodebuf;
                    387:        ctx.outlen = sizeof(encodebuf);
                    388:
                    389:        ctx.salt = (uint8_t *)saltbuf;
                    390:        ctx.saltlen = sizeof(saltbuf);
                    391:
1.7       nia       392:        ctx.pwd = (uint8_t *)pwdbuf;
1.1       jhigh     393:        ctx.pwdlen = sizeof(pwdbuf);
                    394:
                    395:        /* decode salt string to argon2 params */
                    396:        /* argon2 context for param collection */
                    397:        rc = decode_option(&ctx, &atype, salt);
                    398:
                    399:        if (rc < 0) {
1.3       nia       400:                /* unable to parse input params */
1.12      nia       401:                return NULL;
1.1       jhigh     402:        }
                    403:
                    404:        rc = argon2_hash(ctx.t_cost, ctx.m_cost,
1.7       nia       405:            ctx.threads, pw, strlen(pw), ctx.salt, ctx.saltlen,
                    406:            ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf),
                    407:            atype, ctx.version);
1.1       jhigh     408:
                    409:        if (rc != ARGON2_OK) {
1.3       nia       410:                fprintf(stderr, "argon2: failed: %s\n",
                    411:                    argon2_error_message(rc));
1.12      nia       412:                return NULL;
1.1       jhigh     413:        }
                    414:
1.6       nia       415:        memcpy(rbuf, encodebuf, sizeof(encodebuf));
1.1       jhigh     416:
                    417:        /* clear buffers */
1.6       nia       418:        explicit_memset(ebuf, 0, sizeof(ebuf));
1.5       nia       419:        explicit_memset(encodebuf, 0, sizeof(encodebuf));
                    420:        explicit_memset(saltbuf, 0, sizeof(saltbuf));
                    421:        explicit_memset(pwdbuf, 0, sizeof(pwdbuf));
1.1       jhigh     422:
                    423:        /* return encoded str */
                    424:        return rbuf;
                    425: }

CVSweb <webmaster@jp.NetBSD.org>