1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <time.h> 7 #include <pwd.h> 8 #include <errno.h> 9 #include <argon2.h> 10 #include <resolv.h> /* for b64_pton... */ 11 12 #include <err.h> 13 #include "crypt.h" 14 15 /* defaults pulled from run.c */ 16 #define HASHLEN 32 17 #define T_COST_DEF 3 18 #define LOG_M_COST_DEF 12 /* 2^12 = 4 MiB */ 19 #define LANES_DEF 1 20 #define THREADS_DEF 1 21 #define OUTLEN_DEF 32 22 #define MAX_PASS_LEN 128 23 24 #define ARGON2_CONTEXT_INITIALIZER \ 25 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 26 T_COST_DEF, LOG_M_COST_DEF,\ 27 LANES_DEF, THREADS_DEF, \ 28 ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS} 29 30 #define ARGON2_ARGON2_STR "argon2" 31 #define ARGON2_ARGON2I_STR "argon2i" 32 #define ARGON2_ARGON2D_STR "argon2d" 33 #define ARGON2_ARGON2ID_STR "argon2id" 34 35 /* process params to argon2 */ 36 /* we don't force param order as input, */ 37 /* but we do provide the expected order to argon2 api */ 38 static int decode_option(argon2_context * ctx, argon2_type * atype, const char * option) 39 { 40 size_t tmp=0; 41 char * in = 0,*inp; 42 char * a=0; 43 char * p=0; 44 size_t sl; 45 int error=0; 46 47 in = (char *)strdup(option); 48 inp = in; 49 50 if (*inp == '$') inp++; 51 52 a = strsep(&inp, "$"); 53 54 sl = strlen(a); 55 56 if (sl == strlen(ARGON2_ARGON2I_STR) && 57 !(strcmp(ARGON2_ARGON2I_STR, a))) { 58 *atype=Argon2_i; 59 } else if (sl == strlen(ARGON2_ARGON2D_STR) && 60 !(strcmp(ARGON2_ARGON2D_STR, a))) { 61 *atype=Argon2_d; 62 } 63 else if (sl == strlen(ARGON2_ARGON2ID_STR) && 64 !(strcmp(ARGON2_ARGON2ID_STR, a))) { 65 *atype=Argon2_id; 66 } else { /* default to id, we assume simple mistake */ 67 /* don't abandon yet */ 68 *atype=Argon2_id; 69 } 70 71 a = strsep(&inp, "$"); 72 73 /* parse the version number of the hash, if it's there */ 74 if (strncmp(a, "v=", 2) == 0) { 75 a += 2; 76 if ((getnum(a, &tmp))<0) { /* on error, default to current */ 77 /* should start thinking about aborting */ 78 ctx->version = ARGON2_VERSION_10; 79 } else { 80 ctx->version = tmp; 81 } 82 a = strsep(&inp, "$"); 83 } else { 84 /* 85 * This is a parameter list, not a version number, use the 86 * default version. 87 */ 88 ctx->version = ARGON2_VERSION_10; 89 } 90 91 /* parse labelled argon2 params */ 92 /* m_cost (m) 93 * t_cost (t) 94 * threads (p) 95 */ 96 while ((p = strsep(&a, ","))) { 97 switch (*p) { 98 case 'm': 99 p += strlen("m="); 100 if ((getnum(p, &tmp)) < 0) { 101 --error; 102 } else { 103 ctx->m_cost = tmp; 104 } 105 break; 106 case 't': 107 p += strlen("t="); 108 if ((getnum(p, &tmp)) < 0) { 109 --error; 110 } else { 111 ctx->t_cost = tmp; 112 } 113 break; 114 case 'p': 115 p += strlen("p="); 116 if ((getnum(p, &tmp)) < 0) { 117 --error; 118 } else { 119 ctx->threads = tmp; 120 } 121 break; 122 default: 123 return -1; 124 125 } 126 } 127 128 a = strsep(&inp, "$"); 129 130 b64_pton(a, ctx->salt, ctx->saltlen); 131 132 a = strsep(&inp, "$"); 133 134 if (a) { 135 snprintf((char *)ctx->pwd, ctx->pwdlen, "%s", a); 136 } else { 137 /* don't care if passwd hash is missing */ 138 /* if missing, most likely coming from */ 139 /* pwhash or similar */ 140 } 141 142 /* free our token buffer */ 143 free(in); 144 145 /* 0 on success, <0 otherwise */ 146 return error; 147 } 148 149 char * 150 __crypt_argon2(const char *pw, const char * salt) 151 { 152 /* we use the libargon2 api to generate */ 153 /* return code */ 154 int rc=0; 155 /* output buffer */ 156 char ebuf[32]; 157 /* ptr into argon2 encoded buffer */ 158 char * blkp=0; 159 /* argon2 variable, default to id */ 160 argon2_type atype = Argon2_id; 161 /* default to current argon2 version */ 162 /* argon2 context to collect params */ 163 argon2_context ctx = ARGON2_CONTEXT_INITIALIZER; 164 /* argon2 encoded buffer */ 165 char encodebuf[256]; 166 /* argon2 salt buffer */ 167 char saltbuf[128]; 168 /* argon2 pwd buffer */ 169 char pwdbuf[128]; 170 /* returned static buffer */ 171 static char rbuf[512]; 172 173 /* clear buffers */ 174 explicit_memset(rbuf, 0, sizeof(rbuf)); 175 176 /* we use static buffers to avoid allocation */ 177 /* and easier cleanup */ 178 ctx.out = (uint8_t *)ebuf; 179 ctx.outlen = sizeof(ebuf); 180 181 ctx.out = (uint8_t *)encodebuf; 182 ctx.outlen = sizeof(encodebuf); 183 184 ctx.salt = (uint8_t *)saltbuf; 185 ctx.saltlen = sizeof(saltbuf); 186 187 ctx.pwd= (uint8_t *)pwdbuf; 188 ctx.pwdlen = sizeof(pwdbuf); 189 190 /* decode salt string to argon2 params */ 191 /* argon2 context for param collection */ 192 rc = decode_option(&ctx, &atype, salt); 193 194 if (rc < 0) { 195 /* unable to parse input params */ 196 return 0; 197 } 198 199 rc = argon2_hash(ctx.t_cost, ctx.m_cost, 200 ctx.threads, pw, strlen(pw), ctx.salt, strlen((char*)ctx.salt), 201 ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf), atype, ctx.version); 202 203 if (rc != ARGON2_OK) { 204 fprintf(stderr, "argon2: failed: %s\n", 205 argon2_error_message(rc)); 206 return 0; 207 } 208 209 /* get encoded passwd */ 210 if ((blkp = strrchr(encodebuf, '$')) == NULL) { 211 return 0; 212 } 213 214 /* skip over '$' */ 215 blkp++; 216 217 memcpy(rbuf, encodebuf, sizeof(encodebuf)); 218 219 /* clear buffers */ 220 explicit_memset(ebuf, 0, sizeof(ebuf)); 221 explicit_memset(encodebuf, 0, sizeof(encodebuf)); 222 explicit_memset(saltbuf, 0, sizeof(saltbuf)); 223 explicit_memset(pwdbuf, 0, sizeof(pwdbuf)); 224 225 /* return encoded str */ 226 return rbuf; 227 } 228