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