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 *
decode_decimal(const char * str,unsigned long * v)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
decode_string(argon2_context * ctx,const char * str,argon2_type type)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
u32_to_string(char * str,uint32_t x)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
encode_string(char * dst,size_t dst_len,argon2_context * ctx,argon2_type type)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