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@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