Home | History | Annotate | Line # | Download | only in argon2
      1 #include "argon2-encoding.h"
      2 #include "argon2-core.h"
      3 #include "utils.h"
      4 #include <limits.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 /*
     10  * Example code for a decoder and encoder of "hash strings", with Argon2
     11  * parameters.
     12  *
     13  * The code was originally written by Thomas Pornin <pornin (at) bolet.org>,
     14  * to whom comments and remarks may be sent. It is released under what
     15  * should amount to Public Domain or its closest equivalent; the
     16  * following mantra is supposed to incarnate that fact with all the
     17  * proper legal rituals:
     18  *
     19  * ---------------------------------------------------------------------
     20  * This file is provided under the terms of Creative Commons CC0 1.0
     21  * Public Domain Dedication. To the extent possible under law, the
     22  * author (Thomas Pornin) has waived all copyright and related or
     23  * neighboring rights to this file. This work is published from: Canada.
     24  * ---------------------------------------------------------------------
     25  *
     26  * Copyright (c) 2015 Thomas Pornin
     27  */
     28 
     29 /* ==================================================================== */
     30 
     31 /*
     32  * Decode decimal integer from 'str'; the value is written in '*v'.
     33  * Returned value is a pointer to the next non-decimal character in the
     34  * string. If there is no digit at all, or the value encoding is not
     35  * minimal (extra leading zeros), or the value does not fit in an
     36  * 'unsigned long', then NULL is returned.
     37  */
     38 static const char *
     39 decode_decimal(const char *str, unsigned long *v)
     40 {
     41     const char    *orig;
     42     unsigned long  acc;
     43 
     44     acc = 0;
     45     for (orig = str;; str++) {
     46         int c;
     47 
     48         c = *str;
     49         if (c < '0' || c > '9') {
     50             break;
     51         }
     52         c -= '0';
     53         if (acc > (ULONG_MAX / 10)) {
     54             return NULL;
     55         }
     56         acc *= 10;
     57         if ((unsigned long) c > (ULONG_MAX - acc)) {
     58             return NULL;
     59         }
     60         acc += (unsigned long) c;
     61     }
     62     if (str == orig || (*orig == '0' && str != (orig + 1))) {
     63         return NULL;
     64     }
     65     *v = acc;
     66     return str;
     67 }
     68 
     69 /* ==================================================================== */
     70 /*
     71  * Code specific to Argon2.
     72  *
     73  * The code below applies the following format:
     74  *
     75  *  $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>
     76  *
     77  * where <T> is either 'i', <num> is a decimal integer (positive, fits in an
     78  * 'unsigned long') and <bin> is Base64-encoded data (no '=' padding characters,
     79  * no newline or whitespace).
     80  *
     81  * The last two binary chunks (encoded in Base64) are, in that order,
     82  * the salt and the output. Both are required. The binary salt length and the
     83  * output length must be in the allowed ranges defined in argon2.h.
     84  *
     85  * The ctx struct must contain buffers large enough to hold the salt and pwd
     86  * when it is fed into decode_string.
     87  */
     88 
     89 /*
     90  * Decode an Argon2i hash string into the provided structure 'ctx'.
     91  * Returned value is ARGON2_OK on success.
     92  */
     93 int
     94 decode_string(argon2_context *ctx, const char *str, argon2_type type)
     95 {
     96 /* Prefix checking */
     97 #define CC(prefix)                               \
     98     do {                                         \
     99         size_t cc_len = strlen(prefix);          \
    100         if (strncmp(str, prefix, cc_len) != 0) { \
    101             return ARGON2_DECODING_FAIL;         \
    102         }                                        \
    103         str += cc_len;                           \
    104     } while ((void) 0, 0)
    105 
    106 /* Optional prefix checking with supplied code */
    107 #define CC_opt(prefix, code)                     \
    108     do {                                         \
    109         size_t cc_len = strlen(prefix);          \
    110         if (strncmp(str, prefix, cc_len) == 0) { \
    111             str += cc_len;                       \
    112             {                                    \
    113                 code;                            \
    114             }                                    \
    115         }                                        \
    116     } while ((void) 0, 0)
    117 
    118 /* Decoding prefix into decimal */
    119 #define DECIMAL(x)                         \
    120     do {                                   \
    121         unsigned long dec_x;               \
    122         str = decode_decimal(str, &dec_x); \
    123         if (str == NULL) {                 \
    124             return ARGON2_DECODING_FAIL;   \
    125         }                                  \
    126         (x) = dec_x;                       \
    127     } while ((void) 0, 0)
    128 
    129 /* Decoding prefix into uint32_t decimal */
    130 #define DECIMAL_U32(x)                           \
    131     do {                                         \
    132         unsigned long dec_x;                     \
    133         str = decode_decimal(str, &dec_x);       \
    134         if (str == NULL || dec_x > UINT32_MAX) { \
    135             return ARGON2_DECODING_FAIL;         \
    136         }                                        \
    137         (x) = (uint32_t)dec_x;                   \
    138     } while ((void)0, 0)
    139 
    140 /* Decoding base64 into a binary buffer */
    141 #define BIN(buf, max_len, len)                                                   \
    142     do {                                                                         \
    143         size_t bin_len = (max_len);                                              \
    144         const char *str_end;                                                     \
    145         if (sodium_base642bin((buf), (max_len), str, strlen(str), NULL,          \
    146                               &bin_len, &str_end,                                \
    147                               sodium_base64_VARIANT_ORIGINAL_NO_PADDING) != 0 || \
    148             bin_len > UINT32_MAX) {                                              \
    149             return ARGON2_DECODING_FAIL;                                         \
    150         }                                                                        \
    151         (len) = (uint32_t) bin_len;                                              \
    152         str = str_end;                                                           \
    153     } while ((void) 0, 0)
    154 
    155     size_t        maxsaltlen = ctx->saltlen;
    156     size_t        maxoutlen  = ctx->outlen;
    157     int           validation_result;
    158     uint32_t      version = 0;
    159 
    160     ctx->saltlen = 0;
    161     ctx->outlen  = 0;
    162 
    163     if (type == Argon2_id) {
    164         CC("$argon2id");
    165     } else if (type == Argon2_i) {
    166         CC("$argon2i");
    167     } else {
    168         return ARGON2_INCORRECT_TYPE;
    169     }
    170     CC("$v=");
    171     DECIMAL_U32(version);
    172     if (version != ARGON2_VERSION_NUMBER) {
    173         return ARGON2_INCORRECT_TYPE;
    174     }
    175     CC("$m=");
    176     DECIMAL_U32(ctx->m_cost);
    177     if (ctx->m_cost > UINT32_MAX) {
    178         return ARGON2_INCORRECT_TYPE;
    179     }
    180     CC(",t=");
    181     DECIMAL_U32(ctx->t_cost);
    182     if (ctx->t_cost > UINT32_MAX) {
    183         return ARGON2_INCORRECT_TYPE;
    184     }
    185     CC(",p=");
    186     DECIMAL_U32(ctx->lanes);
    187     if (ctx->lanes > UINT32_MAX) {
    188         return ARGON2_INCORRECT_TYPE;
    189     }
    190     ctx->threads = ctx->lanes;
    191 
    192     CC("$");
    193     BIN(ctx->salt, maxsaltlen, ctx->saltlen);
    194     CC("$");
    195     BIN(ctx->out, maxoutlen, ctx->outlen);
    196     validation_result = validate_inputs(ctx);
    197     if (validation_result != ARGON2_OK) {
    198         return validation_result;
    199     }
    200     if (*str == 0) {
    201         return ARGON2_OK;
    202     }
    203     return ARGON2_DECODING_FAIL;
    204 
    205 #undef CC
    206 #undef CC_opt
    207 #undef DECIMAL
    208 #undef BIN
    209 }
    210 
    211 #define U32_STR_MAXSIZE 11U
    212 
    213 static void
    214 u32_to_string(char *str, uint32_t x)
    215 {
    216     char   tmp[U32_STR_MAXSIZE - 1U];
    217     size_t i;
    218 
    219     i = sizeof tmp;
    220     do {
    221         tmp[--i] = (x % (uint32_t) 10U) + '0';
    222         x /= (uint32_t) 10U;
    223     } while (x != 0U && i != 0U);
    224     memcpy(str, &tmp[i], (sizeof tmp) - i);
    225     str[(sizeof tmp) - i] = 0;
    226 }
    227 
    228 /*
    229  * Encode an argon2i hash string into the provided buffer. 'dst_len'
    230  * contains the size, in characters, of the 'dst' buffer; if 'dst_len'
    231  * is less than the number of required characters (including the
    232  * terminating 0), then this function returns 0.
    233  *
    234  * If pp->output_len is 0, then the hash string will be a salt string
    235  * (no output). if pp->salt_len is also 0, then the string will be a
    236  * parameter-only string (no salt and no output).
    237  *
    238  * On success, ARGON2_OK is returned.
    239  */
    240 int
    241 encode_string(char *dst, size_t dst_len, argon2_context *ctx, argon2_type type)
    242 {
    243 #define SS(str)                          \
    244     do {                                 \
    245         size_t pp_len = strlen(str);     \
    246         if (pp_len >= dst_len) {         \
    247             return ARGON2_ENCODING_FAIL; \
    248         }                                \
    249         memcpy(dst, str, pp_len + 1);    \
    250         dst += pp_len;                   \
    251         dst_len -= pp_len;               \
    252     } while ((void) 0, 0)
    253 
    254 #define SX(x)                      \
    255     do {                           \
    256         char tmp[U32_STR_MAXSIZE]; \
    257         u32_to_string(tmp, x);     \
    258         SS(tmp);                   \
    259     } while ((void) 0, 0)
    260 
    261 #define SB(buf, len)                                                                \
    262     do {                                                                            \
    263         size_t sb_len;                                                              \
    264         if (sodium_bin2base64(dst, dst_len, (buf), (len),                           \
    265                               sodium_base64_VARIANT_ORIGINAL_NO_PADDING) == NULL) { \
    266             return ARGON2_ENCODING_FAIL;                                            \
    267         }                                                                           \
    268         sb_len = strlen(dst);                                                       \
    269         dst += sb_len;                                                              \
    270         dst_len -= sb_len;                                                          \
    271     } while ((void) 0, 0)
    272 
    273     int validation_result;
    274 
    275     switch (type) {
    276     case Argon2_id:
    277         SS("$argon2id$v="); break;
    278     case Argon2_i:
    279         SS("$argon2i$v="); break;
    280     default:
    281         return ARGON2_ENCODING_FAIL;
    282     }
    283     validation_result = validate_inputs(ctx);
    284     if (validation_result != ARGON2_OK) {
    285         return validation_result;
    286     }
    287     SX(ARGON2_VERSION_NUMBER);
    288     SS("$m=");
    289     SX(ctx->m_cost);
    290     SS(",t=");
    291     SX(ctx->t_cost);
    292     SS(",p=");
    293     SX(ctx->lanes);
    294 
    295     SS("$");
    296     SB(ctx->salt, ctx->saltlen);
    297 
    298     SS("$");
    299     SB(ctx->out, ctx->outlen);
    300     return ARGON2_OK;
    301 
    302 #undef SS
    303 #undef SX
    304 #undef SB
    305 }
    306