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