crypt-argon2.c revision 1.1.4.2 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