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