/* argon2.c - Password module for argon2 */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2017-2022 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ #include "portable.h" #ifdef SLAPD_PWMOD_PW_ARGON2 #include "ac/string.h" #include "lber_pvt.h" #include "lutil.h" #include "slap.h" #include #include #ifdef HAVE_LIBARGON2 #include /* * For now, we hardcode the default values from the argon2 command line tool * (as of argon2 release 20161029) */ #define SLAPD_ARGON2_ITERATIONS 3 #define SLAPD_ARGON2_MEMORY (1 << 12) #define SLAPD_ARGON2_PARALLELISM 1 #define SLAPD_ARGON2_SALT_LENGTH 16 #define SLAPD_ARGON2_HASH_LENGTH 32 #else /* !HAVE_LIBARGON2 */ #include /* * Or libsodium interactive settings */ #define SLAPD_ARGON2_ITERATIONS crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE #define SLAPD_ARGON2_MEMORY (crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE / 1024) #define SLAPD_ARGON2_PARALLELISM 1 #define SLAPD_ARGON2_SALT_LENGTH crypto_pwhash_argon2id_SALTBYTES #define SLAPD_ARGON2_HASH_LENGTH 32 #endif static unsigned long iterations = SLAPD_ARGON2_ITERATIONS; static unsigned long memory = SLAPD_ARGON2_MEMORY; static unsigned long parallelism = SLAPD_ARGON2_PARALLELISM; const struct berval slapd_argon2_scheme = BER_BVC("{ARGON2}"); static int slapd_argon2_hash( const struct berval *scheme, const struct berval *passwd, struct berval *hash, const char **text ) { /* * Duplicate these values here so future code which allows * configuration has an easier time. */ uint32_t salt_length, hash_length; char *p; int rc = LUTIL_PASSWD_ERR; #ifdef HAVE_LIBARGON2 struct berval salt; size_t encoded_length; salt_length = SLAPD_ARGON2_SALT_LENGTH; hash_length = SLAPD_ARGON2_HASH_LENGTH; encoded_length = argon2_encodedlen( iterations, memory, parallelism, salt_length, hash_length, Argon2_id ); salt.bv_len = salt_length; salt.bv_val = ber_memalloc( salt.bv_len ); if ( salt.bv_val == NULL ) { return LUTIL_PASSWD_ERR; } if ( lutil_entropy( (unsigned char*)salt.bv_val, salt.bv_len ) ) { ber_memfree( salt.bv_val ); return LUTIL_PASSWD_ERR; } p = hash->bv_val = ber_memalloc( scheme->bv_len + encoded_length ); if ( p == NULL ) { ber_memfree( salt.bv_val ); return LUTIL_PASSWD_ERR; } AC_MEMCPY( p, scheme->bv_val, scheme->bv_len ); p += scheme->bv_len; /* * Do the actual heavy lifting */ if ( argon2i_hash_encoded( iterations, memory, parallelism, passwd->bv_val, passwd->bv_len, salt.bv_val, salt_length, hash_length, p, encoded_length ) == 0 ) { rc = LUTIL_PASSWD_OK; } hash->bv_len = scheme->bv_len + encoded_length; ber_memfree( salt.bv_val ); #else /* !HAVE_LIBARGON2 */ /* Not exposed by libsodium salt_length = SLAPD_ARGON2_SALT_LENGTH; hash_length = SLAPD_ARGON2_HASH_LENGTH; */ p = hash->bv_val = ber_memalloc( scheme->bv_len + crypto_pwhash_STRBYTES ); if ( p == NULL ) { return LUTIL_PASSWD_ERR; } AC_MEMCPY( hash->bv_val, scheme->bv_val, scheme->bv_len ); p += scheme->bv_len; if ( crypto_pwhash_str_alg( p, passwd->bv_val, passwd->bv_len, iterations, memory * 1024, crypto_pwhash_ALG_ARGON2ID13 ) == 0 ) { hash->bv_len = strlen( hash->bv_val ); rc = LUTIL_PASSWD_OK; } #endif if ( rc ) { ber_memfree( hash->bv_val ); return LUTIL_PASSWD_ERR; } return LUTIL_PASSWD_OK; } static int slapd_argon2_verify( const struct berval *scheme, const struct berval *passwd, const struct berval *cred, const char **text ) { int rc = LUTIL_PASSWD_ERR; #ifdef HAVE_LIBARGON2 if ( strncmp( passwd->bv_val, "$argon2i$", STRLENOF("$argon2i$") ) == 0 ) { rc = argon2i_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); } else if ( strncmp( passwd->bv_val, "$argon2d$", STRLENOF("$argon2d$") ) == 0 ) { rc = argon2d_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); } else if ( strncmp( passwd->bv_val, "$argon2id$", STRLENOF("$argon2id$") ) == 0 ) { rc = argon2id_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); } #else /* !HAVE_LIBARGON2 */ rc = crypto_pwhash_str_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); #endif if ( rc ) { return LUTIL_PASSWD_ERR; } return LUTIL_PASSWD_OK; } int init_module( int argc, char *argv[] ) { int i; #ifdef HAVE_LIBSODIUM if ( sodium_init() == -1 ) { return -1; } #endif for ( i=0; i < argc; i++ ) { char *p; unsigned long value; switch ( *argv[i] ) { case 'm': p = strchr( argv[i], '=' ); if ( !p || lutil_atoulx( &value, p+1, 0 ) ) { return -1; } memory = value; break; case 't': p = strchr( argv[i], '=' ); if ( !p || lutil_atoulx( &value, p+1, 0 ) ) { return -1; } iterations = value; break; case 'p': p = strchr( argv[i], '=' ); if ( !p || lutil_atoulx( &value, p+1, 0 ) ) { return -1; } parallelism = value; break; default: return -1; } } #ifndef HAVE_LIBARGON2 /* At the moment, we can only use libargon2 to set parallelism for new * hashes */ if ( parallelism != SLAPD_ARGON2_PARALLELISM ) { Debug( LDAP_DEBUG_ANY, "pw-argon2: " "non-default parallelism only supported when linked with " "libargon2, got p=%lu\n", parallelism ); if ( (slapMode & SLAP_MODE) != SLAP_TOOL_MODE || slapTool == SLAPPASSWD || slapTool == SLAPTEST ) { return 1; } } #endif return lutil_passwd_add( (struct berval *)&slapd_argon2_scheme, slapd_argon2_verify, slapd_argon2_hash ); } #endif /* SLAPD_OVER_PW_ARGON2 */