1/*	$NetBSD: argon2.c,v 1.2 2021/08/14 16:15:02 christos Exp $	*/
2
3/* argon2.c - Password module for argon2 */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2017-2021 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include <sys/cdefs.h>
20__RCSID("$NetBSD: argon2.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
21
22#include "portable.h"
23#ifdef SLAPD_PWMOD_PW_ARGON2
24#include "ac/string.h"
25#include "lber_pvt.h"
26#include "lutil.h"
27
28#include "slap.h"
29
30#include <stdint.h>
31#include <stdlib.h>
32
33#ifdef HAVE_LIBARGON2
34#include <argon2.h>
35
36/*
37 * For now, we hardcode the default values from the argon2 command line tool
38 * (as of argon2 release 20161029)
39 */
40#define SLAPD_ARGON2_ITERATIONS 3
41#define SLAPD_ARGON2_MEMORY (1 << 12)
42#define SLAPD_ARGON2_PARALLELISM 1
43#define SLAPD_ARGON2_SALT_LENGTH 16
44#define SLAPD_ARGON2_HASH_LENGTH 32
45
46#else /* !HAVE_LIBARGON2 */
47#include <sodium.h>
48
49/*
50 * Or libsodium interactive settings
51 */
52#define SLAPD_ARGON2_ITERATIONS crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE
53#define SLAPD_ARGON2_MEMORY (crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE / 1024)
54#define SLAPD_ARGON2_PARALLELISM 1
55#define SLAPD_ARGON2_SALT_LENGTH crypto_pwhash_argon2id_SALTBYTES
56#define SLAPD_ARGON2_HASH_LENGTH 32
57
58#endif
59
60static unsigned long iterations = SLAPD_ARGON2_ITERATIONS;
61static unsigned long memory = SLAPD_ARGON2_MEMORY;
62static unsigned long parallelism = SLAPD_ARGON2_PARALLELISM;
63
64const struct berval slapd_argon2_scheme = BER_BVC("{ARGON2}");
65
66static int
67slapd_argon2_hash(
68		const struct berval *scheme,
69		const struct berval *passwd,
70		struct berval *hash,
71		const char **text )
72{
73
74	/*
75	 * Duplicate these values here so future code which allows
76	 * configuration has an easier time.
77	 */
78	uint32_t salt_length, hash_length;
79	char *p;
80	int rc = LUTIL_PASSWD_ERR;
81
82#ifdef HAVE_LIBARGON2
83	struct berval salt;
84	size_t encoded_length;
85
86	salt_length = SLAPD_ARGON2_SALT_LENGTH;
87	hash_length = SLAPD_ARGON2_HASH_LENGTH;
88
89	encoded_length = argon2_encodedlen( iterations, memory, parallelism,
90			salt_length, hash_length, Argon2_id );
91
92	salt.bv_len = salt_length;
93	salt.bv_val = ber_memalloc( salt.bv_len );
94
95	if ( salt.bv_val == NULL ) {
96		return LUTIL_PASSWD_ERR;
97	}
98
99	if ( lutil_entropy( (unsigned char*)salt.bv_val, salt.bv_len ) ) {
100		ber_memfree( salt.bv_val );
101		return LUTIL_PASSWD_ERR;
102	}
103
104	p = hash->bv_val = ber_memalloc( scheme->bv_len + encoded_length );
105	if ( p == NULL ) {
106		ber_memfree( salt.bv_val );
107		return LUTIL_PASSWD_ERR;
108	}
109
110	AC_MEMCPY( p, scheme->bv_val, scheme->bv_len );
111	p += scheme->bv_len;
112
113	/*
114	 * Do the actual heavy lifting
115	 */
116	if ( argon2i_hash_encoded( iterations, memory, parallelism,
117				passwd->bv_val, passwd->bv_len,
118				salt.bv_val, salt_length, hash_length,
119				p, encoded_length ) == 0 ) {
120		rc = LUTIL_PASSWD_OK;
121	}
122	hash->bv_len = scheme->bv_len + encoded_length;
123	ber_memfree( salt.bv_val );
124
125#else /* !HAVE_LIBARGON2 */
126	/* Not exposed by libsodium
127	salt_length = SLAPD_ARGON2_SALT_LENGTH;
128	hash_length = SLAPD_ARGON2_HASH_LENGTH;
129	*/
130
131	p = hash->bv_val = ber_memalloc( scheme->bv_len + crypto_pwhash_STRBYTES );
132	if ( p == NULL ) {
133		return LUTIL_PASSWD_ERR;
134	}
135
136	AC_MEMCPY( hash->bv_val, scheme->bv_val, scheme->bv_len );
137	p += scheme->bv_len;
138
139	if ( crypto_pwhash_str_alg( p, passwd->bv_val, passwd->bv_len,
140				iterations, memory * 1024,
141				crypto_pwhash_ALG_ARGON2ID13 ) == 0 ) {
142		hash->bv_len = strlen( hash->bv_val );
143		rc = LUTIL_PASSWD_OK;
144	}
145#endif
146
147	if ( rc ) {
148		ber_memfree( hash->bv_val );
149		return LUTIL_PASSWD_ERR;
150	}
151
152	return LUTIL_PASSWD_OK;
153}
154
155static int
156slapd_argon2_verify(
157		const struct berval *scheme,
158		const struct berval *passwd,
159		const struct berval *cred,
160		const char **text )
161{
162	int rc = LUTIL_PASSWD_ERR;
163
164#ifdef HAVE_LIBARGON2
165	if ( strncmp( passwd->bv_val, "$argon2i$", STRLENOF("$argon2i$") ) == 0 ) {
166		rc = argon2i_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
167	} else if ( strncmp( passwd->bv_val, "$argon2d$", STRLENOF("$argon2d$") ) == 0 ) {
168		rc = argon2d_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
169	} else if ( strncmp( passwd->bv_val, "$argon2id$", STRLENOF("$argon2id$") ) == 0 ) {
170		rc = argon2id_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
171	}
172#else /* !HAVE_LIBARGON2 */
173	rc = crypto_pwhash_str_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
174#endif
175
176	if ( rc ) {
177		return LUTIL_PASSWD_ERR;
178	}
179	return LUTIL_PASSWD_OK;
180}
181
182int init_module( int argc, char *argv[] )
183{
184	int i;
185
186#ifdef HAVE_LIBSODIUM
187	if ( sodium_init() == -1 ) {
188		return -1;
189	}
190#endif
191
192	for ( i=0; i < argc; i++ ) {
193		char *p;
194		unsigned long value;
195
196		switch ( *argv[i] ) {
197			case 'm':
198				p = strchr( argv[i], '=' );
199				if ( !p || lutil_atoulx( &value, p+1, 0 ) ) {
200					return -1;
201				}
202				memory = value;
203				break;
204
205			case 't':
206				p = strchr( argv[i], '=' );
207				if ( !p || lutil_atoulx( &value, p+1, 0 ) ) {
208					return -1;
209				}
210				iterations = value;
211				break;
212
213			case 'p':
214				p = strchr( argv[i], '=' );
215				if ( !p || lutil_atoulx( &value, p+1, 0 ) ) {
216					return -1;
217				}
218				parallelism = value;
219				break;
220
221			default:
222				return -1;
223		}
224	}
225
226#ifndef HAVE_LIBARGON2
227	/* At the moment, we can only use libargon2 to set parallelism for new
228	 * hashes */
229	if ( parallelism != SLAPD_ARGON2_PARALLELISM ) {
230		Debug( LDAP_DEBUG_ANY, "pw-argon2: "
231				"non-default parallelism only supported when linked with "
232				"libargon2, got p=%lu\n",
233				parallelism );
234
235		if ( (slapMode & SLAP_MODE) != SLAP_TOOL_MODE ||
236				slapTool == SLAPPASSWD || slapTool == SLAPTEST ) {
237			return 1;
238		}
239	}
240#endif
241
242	return lutil_passwd_add( (struct berval *)&slapd_argon2_scheme,
243			slapd_argon2_verify, slapd_argon2_hash );
244}
245#endif /* SLAPD_OVER_PW_ARGON2 */
246