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