xref: /netbsd-src/lib/libcrypt/crypt-argon2.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
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