Home | History | Annotate | Line # | Download | only in pwmods
      1 /*	$NetBSD: argon2.c,v 1.3 2025/09/05 21:16:32 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-2024 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.3 2025/09/05 21:16:32 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  * Use OWASP recommended values (retrieved on 2023-08-07)
     38  * @see https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
     39  */
     40 #define SLAPD_ARGON2_ITERATIONS 5
     41 #define SLAPD_ARGON2_MEMORY 7168
     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 
     60 static unsigned long iterations = SLAPD_ARGON2_ITERATIONS;
     61 static unsigned long memory = SLAPD_ARGON2_MEMORY;
     62 static unsigned long parallelism = SLAPD_ARGON2_PARALLELISM;
     63 
     64 const struct berval slapd_argon2_scheme = BER_BVC("{ARGON2}");
     65 
     66 static int
     67 slapd_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 ( argon2id_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 
    155 static int
    156 slapd_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 
    182 int 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