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