xref: /netbsd-src/lib/libcrypt/crypt-argon2.c (revision 4d92dbe90eae91beded70db613c2e6a1fd6dd84a)
1*4d92dbe9Sriastradh /*	$NetBSD: crypt-argon2.c,v 1.22 2024/07/23 22:37:11 riastradh Exp $	*/
281085b6fSriastradh 
32e485d4aSjhigh /*
42e485d4aSjhigh  * Copyright (c) 2009 The NetBSD Foundation, Inc.
52e485d4aSjhigh  * All rights reserved.
62e485d4aSjhigh  *
72e485d4aSjhigh  * Redistribution and use in source and binary forms, with or without
82e485d4aSjhigh  * modification, are permitted provided that the following conditions
92e485d4aSjhigh  * are met:
102e485d4aSjhigh  * 1. Redistributions of source code must retain the above copyright
112e485d4aSjhigh  *    notice, this list of conditions and the following disclaimer.
122e485d4aSjhigh  * 2. Redistributions in binary form must reproduce the above copyright
132e485d4aSjhigh  *    notice, this list of conditions and the following disclaimer in the
142e485d4aSjhigh  *    documentation and/or other materials provided with the distribution.
152e485d4aSjhigh  *
162e485d4aSjhigh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
172e485d4aSjhigh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
182e485d4aSjhigh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
192e485d4aSjhigh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
202e485d4aSjhigh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
212e485d4aSjhigh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
222e485d4aSjhigh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
232e485d4aSjhigh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
242e485d4aSjhigh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
252e485d4aSjhigh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
262e485d4aSjhigh  * POSSIBILITY OF SUCH DAMAGE.
272e485d4aSjhigh  */
282e485d4aSjhigh 
2981085b6fSriastradh #include <sys/cdefs.h>
30*4d92dbe9Sriastradh __RCSID("$NetBSD: crypt-argon2.c,v 1.22 2024/07/23 22:37:11 riastradh Exp $");
3181085b6fSriastradh 
3208100292Snia #include <sys/resource.h>
3308100292Snia #include <sys/param.h>
3408100292Snia #include <sys/sysctl.h>
3508100292Snia #include <sys/syslimits.h>
3608100292Snia 
37b302373fSjhigh #include <stdlib.h>
38b302373fSjhigh #include <stdio.h>
39b302373fSjhigh #include <unistd.h>
40b302373fSjhigh #include <stdio.h>
41b302373fSjhigh #include <string.h>
42b302373fSjhigh #include <time.h>
43b302373fSjhigh #include <pwd.h>
44b302373fSjhigh #include <errno.h>
45b302373fSjhigh #include <argon2.h>
46b302373fSjhigh 
47b302373fSjhigh #include <err.h>
48b302373fSjhigh #include "crypt.h"
49b302373fSjhigh 
5008100292Snia crypt_private int
5108100292Snia estimate_argon2_params(argon2_type, uint32_t *,
5208100292Snia     uint32_t *, uint32_t *);
5308100292Snia 
54b302373fSjhigh /* defaults pulled from run.c */
55b302373fSjhigh #define HASHLEN		32
56b302373fSjhigh #define T_COST_DEF 	3
57b302373fSjhigh #define LOG_M_COST_DEF 	12 /* 2^12 = 4 MiB */
58b302373fSjhigh #define LANES_DEF 	1
59b302373fSjhigh #define THREADS_DEF 	1
60b302373fSjhigh #define OUTLEN_DEF 	32
61b302373fSjhigh #define MAX_PASS_LEN 	128
62b302373fSjhigh 
63b302373fSjhigh #define ARGON2_CONTEXT_INITIALIZER	\
64b302373fSjhigh 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
65b302373fSjhigh 	T_COST_DEF, LOG_M_COST_DEF,\
66b302373fSjhigh 	LANES_DEF, THREADS_DEF, \
67b302373fSjhigh 	ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
68b302373fSjhigh 
69b302373fSjhigh #define ARGON2_ARGON2_STR	"argon2"
70b302373fSjhigh #define ARGON2_ARGON2I_STR	"argon2i"
71b302373fSjhigh #define ARGON2_ARGON2D_STR	"argon2d"
72b302373fSjhigh #define ARGON2_ARGON2ID_STR	"argon2id"
73b302373fSjhigh 
7408100292Snia /*
7508100292Snia  * Unpadded Base64 calculations are taken from the Apache2/CC-0
7608100292Snia  * licensed libargon2 for compatibility
7708100292Snia  */
78f7145efdSnia 
79f7145efdSnia /*
80f7145efdSnia  * Some macros for constant-time comparisons. These work over values in
81f7145efdSnia  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
82f7145efdSnia  */
83f7145efdSnia #define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
84f7145efdSnia #define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
85f7145efdSnia #define GE(x, y) (GT(y, x) ^ 0xFF)
86f7145efdSnia #define LT(x, y) GT(y, x)
87f7145efdSnia #define LE(x, y) GE(y, x)
88f7145efdSnia 
89f7145efdSnia static unsigned
90f7145efdSnia b64_char_to_byte(int c)
91f7145efdSnia {
92f7145efdSnia     unsigned x;
93f7145efdSnia 
94f7145efdSnia     x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
95f7145efdSnia         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
96f7145efdSnia         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
97f7145efdSnia         (EQ(c, '/') & 63);
98f7145efdSnia     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
99f7145efdSnia }
100f7145efdSnia 
101f7145efdSnia static const char *
102f7145efdSnia from_base64(void *dst, size_t *dst_len, const char *src)
103f7145efdSnia {
104f7145efdSnia 	size_t len;
105f7145efdSnia 	unsigned char *buf;
106f7145efdSnia 	unsigned acc, acc_len;
107f7145efdSnia 
108f7145efdSnia 	buf = (unsigned char *)dst;
109f7145efdSnia 	len = 0;
110f7145efdSnia 	acc = 0;
111f7145efdSnia 	acc_len = 0;
112f7145efdSnia 	for (;;) {
113f7145efdSnia 		unsigned d;
114f7145efdSnia 
115f7145efdSnia 		d = b64_char_to_byte(*src);
116f7145efdSnia 		if (d == 0xFF) {
117f7145efdSnia 			break;
118f7145efdSnia 		}
119f7145efdSnia 		src++;
120f7145efdSnia 		acc = (acc << 6) + d;
121f7145efdSnia 		acc_len += 6;
122f7145efdSnia 		if (acc_len >= 8) {
123f7145efdSnia 			acc_len -= 8;
124f7145efdSnia 			if ((len++) >= *dst_len) {
125f7145efdSnia 				return NULL;
126f7145efdSnia 			}
127f7145efdSnia 			*buf++ = (acc >> acc_len) & 0xFF;
128f7145efdSnia 		}
129f7145efdSnia 	}
130f7145efdSnia 
131f7145efdSnia 	/*
132f7145efdSnia 	 * If the input length is equal to 1 modulo 4 (which is
133f7145efdSnia 	 * invalid), then there will remain 6 unprocessed bits;
134f7145efdSnia 	 * otherwise, only 0, 2 or 4 bits are buffered. The buffered
135f7145efdSnia 	 * bits must also all be zero.
136f7145efdSnia 	 */
137f7145efdSnia 	if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
138f7145efdSnia 		return NULL;
139f7145efdSnia 	}
140f7145efdSnia 	*dst_len = len;
141f7145efdSnia 	return src;
142f7145efdSnia }
143f7145efdSnia 
14408100292Snia /*
14508100292Snia  * Used to find default parameters that perform well on the host
14608100292Snia  * machine.  Inputs should dereference to either 0 (to estimate),
14708100292Snia  * or desired value.
14808100292Snia  */
14908100292Snia crypt_private int
15008100292Snia estimate_argon2_params(argon2_type atype, uint32_t *etime,
15108100292Snia     uint32_t *ememory, uint32_t *ethreads)
15208100292Snia {
15308100292Snia 	const int mib[] = { CTL_HW, HW_USERMEM64 };
15408100292Snia 	struct timespec tp1, tp2, delta;
15508100292Snia 	char tmp_salt[16];
15608100292Snia 	char tmp_pwd[16];
15708100292Snia 	uint32_t tmp_hash[32];
15808100292Snia 	char tmp_encoded[256];
15908100292Snia 	struct rlimit rlim;
1605a3dc900Snia 	uint64_t max_mem; /* usermem64 returns bytes */
16108100292Snia 	size_t max_mem_sz = sizeof(max_mem);
16208100292Snia 	/* low values from argon2 test suite... */
1635a3dc900Snia 	uint32_t memory = 256; /* 256k; argon2 wants kilobytes */
164ac442901Snia 	uint32_t time = 3;
16508100292Snia 	uint32_t threads = 1;
16608100292Snia 
16708100292Snia 	if (*ememory < ARGON2_MIN_MEMORY) {
16808100292Snia 		/*
16908100292Snia 		 * attempt to find a reasonble bound for memory use
17008100292Snia 		 */
17108100292Snia 		if (sysctl(mib, __arraycount(mib),
17208100292Snia 		    &max_mem, &max_mem_sz, NULL, 0) < 0) {
17308100292Snia 			goto error;
17408100292Snia 		}
17508100292Snia 		if (getrlimit(RLIMIT_AS, &rlim) < 0)
17608100292Snia 			goto error;
17708100292Snia 		if (max_mem > rlim.rlim_cur && rlim.rlim_cur != RLIM_INFINITY)
17808100292Snia 			max_mem = rlim.rlim_cur;
17908100292Snia 
18008100292Snia 		/*
18108100292Snia 		 * Note that adding memory also greatly slows the algorithm.
18208100292Snia 		 * Do we need to be concerned about memory usage during
18308100292Snia 		 * concurrent connections?
18408100292Snia 		 */
1855a3dc900Snia 		max_mem /= 1000000; /* bytes down to mb */
18608100292Snia 		if (max_mem > 30000) {
187cb1631f6Snia 			memory = 32768;
188cb1631f6Snia 		} else if (max_mem > 15000) {
189cb1631f6Snia 			memory = 16384;
19008100292Snia 		} else if (max_mem > 7000) {
191cb1631f6Snia 			memory = 8192;
192cb1631f6Snia 		} else if (max_mem > 3000) {
19308100292Snia 			memory = 4096;
194cb1631f6Snia 		} else if (max_mem > 900) {
195cb1631f6Snia 			memory = 1024;
19608100292Snia 		} else if (max_mem > 24) {
19708100292Snia 			memory = 256;
19808100292Snia 		} else {
19908100292Snia 			memory = ARGON2_MIN_MEMORY;
20008100292Snia 		}
20108100292Snia 	} else {
20208100292Snia 		memory = *ememory;
20308100292Snia 	}
20408100292Snia 
20508100292Snia 	if (*etime < ARGON2_MIN_TIME) {
20608100292Snia 		/*
20708100292Snia 		 * just fill these with random stuff since we'll immediately
20808100292Snia 		 * discard them after calculating hashes for 1 second
20908100292Snia 		 */
21008100292Snia 		arc4random_buf(tmp_pwd, sizeof(tmp_pwd));
21108100292Snia 		arc4random_buf(tmp_salt, sizeof(tmp_salt));
21208100292Snia 
21308100292Snia 		if (clock_gettime(CLOCK_MONOTONIC, &tp1) == -1)
21408100292Snia 			goto error;
2153fb27417Smlelstv 		for (; time < ARGON2_MAX_TIME; ++time) {
21608100292Snia 			if (argon2_hash(time, memory, threads,
21708100292Snia 			    tmp_pwd, sizeof(tmp_pwd),
21808100292Snia 			    tmp_salt, sizeof(tmp_salt),
21908100292Snia 			    tmp_hash, sizeof(tmp_hash),
22008100292Snia 			    tmp_encoded, sizeof(tmp_encoded),
22108100292Snia 			    atype, ARGON2_VERSION_NUMBER) != ARGON2_OK) {
22208100292Snia 				goto reset;
22308100292Snia 			}
22408100292Snia 			if (clock_gettime(CLOCK_MONOTONIC, &tp2) == -1)
22508100292Snia 				break;
22608100292Snia 			if (timespeccmp(&tp1, &tp2, >))
22708100292Snia 				break; /* broken system... */
22808100292Snia 			timespecsub(&tp2, &tp1, &delta);
2293fb27417Smlelstv 			if (delta.tv_sec >= 1)
2303fb27417Smlelstv 				break;
23108100292Snia 		}
23208100292Snia 	} else {
23308100292Snia 		time = *etime;
23408100292Snia 	}
23508100292Snia 
23608100292Snia error:
23708100292Snia 	*etime = time;
23808100292Snia 	*ememory = memory;
23908100292Snia 	*ethreads = threads;
24008100292Snia 	return 0;
24108100292Snia reset:
242ac442901Snia 	time = 3;
24308100292Snia 	memory = 256;
24408100292Snia 	threads = 1;
24508100292Snia 	goto error;
24608100292Snia }
24708100292Snia 
24808100292Snia 
249b302373fSjhigh /* process params to argon2 */
250b302373fSjhigh /* we don't force param order as input, */
251b302373fSjhigh /* but we do provide the expected order to argon2 api */
252f7145efdSnia static int
253f7145efdSnia decode_option(argon2_context *ctx, argon2_type *atype, const char *option)
254b302373fSjhigh {
255b302373fSjhigh 	size_t tmp = 0;
2568012ca3fSmsaitoh         char *in = 0, *inp;
257b302373fSjhigh         char *a = 0;
258b302373fSjhigh         char *p = 0;
259b302373fSjhigh 	size_t sl;
260b302373fSjhigh 	int error = 0;
261b302373fSjhigh 
262b302373fSjhigh         in = (char *)strdup(option);
263b302373fSjhigh 	inp = in;
264b302373fSjhigh 
265b302373fSjhigh 	if (*inp == '$') inp++;
266b302373fSjhigh 
267b302373fSjhigh 	a = strsep(&inp, "$");
268b302373fSjhigh 
269b302373fSjhigh 	sl = strlen(a);
270b302373fSjhigh 
271b302373fSjhigh 	if (sl == strlen(ARGON2_ARGON2I_STR) &&
272b302373fSjhigh 	   !(strcmp(ARGON2_ARGON2I_STR, a))) {
273b302373fSjhigh 		*atype=Argon2_i;
274b302373fSjhigh 	} else if (sl == strlen(ARGON2_ARGON2D_STR) &&
275b302373fSjhigh 	        !(strcmp(ARGON2_ARGON2D_STR, a))) {
276b302373fSjhigh 		*atype=Argon2_d;
277b302373fSjhigh 	}
278b302373fSjhigh 	else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
279b302373fSjhigh 	        !(strcmp(ARGON2_ARGON2ID_STR, a))) {
280b302373fSjhigh 		*atype=Argon2_id;
281b302373fSjhigh 	} else { /* default to id, we assume simple mistake */
282b302373fSjhigh 		/* don't abandon yet */
283b302373fSjhigh 		*atype=Argon2_id;
284b302373fSjhigh 	}
285b302373fSjhigh 
286b302373fSjhigh 	a = strsep(&inp, "$");
287b302373fSjhigh 
288d595d5c3Snia 	/* parse the version number of the hash, if it's there */
289d595d5c3Snia 	if (strncmp(a, "v=", 2) == 0) {
290d595d5c3Snia 		a += 2;
291b302373fSjhigh 		if ((getnum(a, &tmp))<0) { /* on error, default to current */
292b302373fSjhigh 			/* should start thinking about aborting */
29388cf5018Snia 			ctx->version = ARGON2_VERSION_10;
294b302373fSjhigh 		} else {
295b302373fSjhigh 			ctx->version = tmp;
296b302373fSjhigh 		}
297b302373fSjhigh 		a = strsep(&inp, "$");
298d595d5c3Snia 	} else {
299d595d5c3Snia 		/*
300d595d5c3Snia 		 * This is a parameter list, not a version number, use the
301d595d5c3Snia 		 * default version.
302d595d5c3Snia 		 */
30388cf5018Snia 		ctx->version = ARGON2_VERSION_10;
304d595d5c3Snia 	}
305b302373fSjhigh 
306b302373fSjhigh 	/* parse labelled argon2 params */
307b302373fSjhigh 	/* m_cost (m)
308b302373fSjhigh 	 * t_cost (t)
309b302373fSjhigh 	 * threads (p)
310b302373fSjhigh 	 */
311b302373fSjhigh 	while ((p = strsep(&a, ","))) {
312b302373fSjhigh 		switch (*p) {
313b302373fSjhigh 			case 'm':
314b302373fSjhigh 				p += strlen("m=");
315b302373fSjhigh 				if ((getnum(p, &tmp)) < 0) {
316b302373fSjhigh 					--error;
317b302373fSjhigh 				} else {
318b302373fSjhigh 					ctx->m_cost = tmp;
319b302373fSjhigh 				}
320b302373fSjhigh 				break;
321b302373fSjhigh 			case 't':
322b302373fSjhigh 				p += strlen("t=");
323b302373fSjhigh 				if ((getnum(p, &tmp)) < 0) {
324b302373fSjhigh 					--error;
325b302373fSjhigh 				} else {
326b302373fSjhigh 					ctx->t_cost = tmp;
327b302373fSjhigh 				}
328b302373fSjhigh 				break;
329b302373fSjhigh 			case 'p':
330b302373fSjhigh 				p += strlen("p=");
331b302373fSjhigh 				if ((getnum(p, &tmp)) < 0) {
332b302373fSjhigh 					--error;
333b302373fSjhigh 				} else {
334b302373fSjhigh 					ctx->threads = tmp;
335b302373fSjhigh 				}
336b302373fSjhigh 				break;
337b302373fSjhigh 			default:
338e7c71814Sabs                                 free(in);
339b302373fSjhigh 				return -1;
340b302373fSjhigh 
341b302373fSjhigh 		}
342b302373fSjhigh 	}
343b302373fSjhigh 
344b302373fSjhigh 	a = strsep(&inp, "$");
345c44afc56Smartin 	if (a == NULL) {
34604799ba5Sabs 		free(in);
34704799ba5Sabs 		return -1;
34804799ba5Sabs  	}
349b302373fSjhigh 
350f7145efdSnia 	sl = ctx->saltlen;
351f7145efdSnia 
352e7c71814Sabs 	if (from_base64(ctx->salt, &sl, a) == NULL) {
353e7c71814Sabs 		free(in);
354f7145efdSnia 		return -1;
355e7c71814Sabs 	}
356f7145efdSnia 
357f7145efdSnia 	ctx->saltlen = sl;
358b302373fSjhigh 
359b302373fSjhigh 	a = strsep(&inp, "$");
360b302373fSjhigh 
361d595d5c3Snia 	if (a) {
362b302373fSjhigh 		snprintf((char *)ctx->pwd, ctx->pwdlen, "%s", a);
363b302373fSjhigh 	} else {
364b302373fSjhigh 		/* don't care if passwd hash is missing */
365b302373fSjhigh 		/* if missing, most likely coming from */
366b302373fSjhigh 		/* pwhash or similar */
367b302373fSjhigh 	}
368b302373fSjhigh 
369b302373fSjhigh 	/* free our token buffer */
370b302373fSjhigh         free(in);
371b302373fSjhigh 
372b302373fSjhigh 	/* 0 on success, <0 otherwise */
373b302373fSjhigh         return error;
374b302373fSjhigh }
375b302373fSjhigh 
376f9151ba9Snia crypt_private char *
377b302373fSjhigh __crypt_argon2(const char *pw, const char * salt)
378b302373fSjhigh {
379b302373fSjhigh 	/* we use the libargon2 api to generate */
380b302373fSjhigh 	/* return code */
381b302373fSjhigh 	int rc = 0;
382b302373fSjhigh 	/* output buffer */
383b302373fSjhigh 	char ebuf[32];
384b302373fSjhigh 	/* argon2 variable, default to id */
385b302373fSjhigh 	argon2_type atype = Argon2_id;
386b302373fSjhigh 	/* default to current argon2 version */
387b302373fSjhigh 	/* argon2 context to collect params */
388b302373fSjhigh 	argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
389b302373fSjhigh 	/* argon2 encoded buffer */
390b302373fSjhigh 	char encodebuf[256];
391b302373fSjhigh 	/* argon2 salt buffer */
392b302373fSjhigh 	char saltbuf[128];
393b302373fSjhigh 	/* argon2 pwd buffer */
394b302373fSjhigh 	char pwdbuf[128];
395b302373fSjhigh 	/* returned static buffer */
396b302373fSjhigh 	static char rbuf[512];
397b302373fSjhigh 
398b302373fSjhigh 	/* clear buffers */
39980833458Snia 	explicit_memset(rbuf, 0, sizeof(rbuf));
400b302373fSjhigh 
401b302373fSjhigh 	/* we use static buffers to avoid allocation */
402b302373fSjhigh 	/* and easier cleanup */
403b302373fSjhigh 	ctx.out = (uint8_t *)encodebuf;
404b302373fSjhigh 	ctx.outlen = sizeof(encodebuf);
405b302373fSjhigh 
406b302373fSjhigh 	ctx.salt = (uint8_t *)saltbuf;
407b302373fSjhigh 	ctx.saltlen = sizeof(saltbuf);
408b302373fSjhigh 
409b302373fSjhigh 	ctx.pwd = (uint8_t *)pwdbuf;
410b302373fSjhigh 	ctx.pwdlen = sizeof(pwdbuf);
411b302373fSjhigh 
412b302373fSjhigh 	/* decode salt string to argon2 params */
413b302373fSjhigh 	/* argon2 context for param collection */
414b302373fSjhigh 	rc = decode_option(&ctx, &atype, salt);
415b302373fSjhigh 
416b302373fSjhigh 	if (rc < 0) {
417b302373fSjhigh 		/* unable to parse input params */
418364b5e26Snia 		return NULL;
419b302373fSjhigh 	}
420b302373fSjhigh 
421b302373fSjhigh 	rc = argon2_hash(ctx.t_cost, ctx.m_cost,
422f7145efdSnia 	    ctx.threads, pw, strlen(pw), ctx.salt, ctx.saltlen,
423f7145efdSnia 	    ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf),
424f7145efdSnia 	    atype, ctx.version);
425b302373fSjhigh 
426b302373fSjhigh 	if (rc != ARGON2_OK) {
427d595d5c3Snia 		fprintf(stderr, "argon2: failed: %s\n",
428d595d5c3Snia 		    argon2_error_message(rc));
429364b5e26Snia 		return NULL;
430b302373fSjhigh 	}
431b302373fSjhigh 
43280833458Snia 	memcpy(rbuf, encodebuf, sizeof(encodebuf));
433b302373fSjhigh 
434b302373fSjhigh 	/* clear buffers */
43580833458Snia 	explicit_memset(ebuf, 0, sizeof(ebuf));
436b0735155Snia 	explicit_memset(encodebuf, 0, sizeof(encodebuf));
437b0735155Snia 	explicit_memset(saltbuf, 0, sizeof(saltbuf));
438b0735155Snia 	explicit_memset(pwdbuf, 0, sizeof(pwdbuf));
439b302373fSjhigh 
440b302373fSjhigh 	/* return encoded str */
441b302373fSjhigh 	return rbuf;
442b302373fSjhigh }
443