text: /*
   * SPDX-License-Identifier: ISC
   * SPDX-URL: https://spdx.org/licenses/ISC.html
   *
   * Copyright (C) 2018 Atheme Development Group (https://atheme.github.io/)
   *
   * POSIX-style SHA2-512 crypt(3) wrapper.
   */
  
  #include <atheme.h>
  
  #define CRYPTO_MODULE_NAME "crypto/crypt3-sha2-512"
  
  #ifdef HAVE_CRYPT
  
  #include "crypt3-wrapper.h"
  
  static mowgli_list_t **crypto_conf_table = NULL;
  
  static unsigned int crypt3_md_rounds = CRYPT3_SHA2_ITERCNT_DEF;
  
  static const char * ATHEME_FATTR_WUR
  atheme_crypt3_sha2_512_crypt(const char *const restrict password,
                               const char ATHEME_VATTR_UNUSED *const restrict parameters)
  {
  	unsigned char rawsalt[CRYPT3_SHA2_SALTLEN_RAW];
  	char salt[BASE64_SIZE_STR(sizeof rawsalt)];
  	char parv[PASSLEN + 1];
  
  	(void) atheme_random_buf(rawsalt, sizeof rawsalt);
  
  	if (base64_encode_table(rawsalt, sizeof rawsalt, salt, sizeof salt, BASE64_ALPHABET_CRYPT3) == BASE64_FAIL)
  	{
  		(void) slog(LG_ERROR, "%s: base64_encode_table() failed (BUG!)", MOWGLI_FUNC_NAME);
  		return NULL;
  	}
  
  	if (crypt3_md_rounds == CRYPT3_SHA2_ITERCNT_DEF)
  	{
  		if (snprintf(parv, sizeof parv, CRYPT3_SAVESALT_FORMAT_SHA2_512, salt) > PASSLEN)
  		{
  			(void) slog(LG_ERROR, "%s: snprintf(3) output would have been too long (BUG)", MOWGLI_FUNC_NAME);
  			return NULL;
  		}
  	}
  	else
  	{
  		if (snprintf(parv, sizeof parv, CRYPT3_SAVESALT_FORMAT_SHA2_512_EXT, crypt3_md_rounds, salt) > PASSLEN)
  		{
  			(void) slog(LG_ERROR, "%s: snprintf(3) output would have been too long (BUG)", MOWGLI_FUNC_NAME);
  			return NULL;
  		}
  	}
  
  	// This function logs messages on failure
  	return atheme_crypt3_wrapper(password, parv, MOWGLI_FUNC_NAME);
  }
  
  static bool ATHEME_FATTR_WUR
  atheme_crypt3_sha2_512_verify(const char *const restrict password, const char *const restrict parameters,
                                unsigned int *const restrict flags)
  {
  	unsigned int rounds = CRYPT3_SHA2_ITERCNT_DEF;
  	char hash[BUFSIZE];
  
  	if (sscanf(parameters, CRYPT3_LOADHASH_FORMAT_SHA2_512, hash) != 1)
  	{
  		if (sscanf(parameters, CRYPT3_LOADHASH_FORMAT_SHA2_512_EXT, &rounds, hash) != 2)
  		{
  			(void) slog(LG_DEBUG, "%s: sscanf(3) was unsuccessful", MOWGLI_FUNC_NAME);
  			return false;
  		}
  	}
  	if (strlen(hash) != CRYPT3_LOADHASH_LENGTH_SHA2_512)
  	{
  		(void) slog(LG_DEBUG, "%s: digest not %u characters long", MOWGLI_FUNC_NAME,
  		                      CRYPT3_LOADHASH_LENGTH_SHA2_512);
  		return false;
  	}
  
  	(void) smemzero(hash, sizeof hash);
  
  	*flags |= PWVERIFY_FLAG_MYMODULE;
  
  	const char *const result = atheme_crypt3_wrapper(password, parameters, MOWGLI_FUNC_NAME);
  
  	if (! result)
  		// That function logs messages on failure
  		return false;
  
  	if (strcmp(parameters, result) != 0)
  	{
  		(void) slog(LG_DEBUG, "%s: strcmp(3) mismatch, invalid password?", MOWGLI_FUNC_NAME);
  		(void) slog(LG_DEBUG, "%s: expected '%s', got '%s'", MOWGLI_FUNC_NAME, parameters, result);
  		return false;
  	}
  
  	if (rounds != crypt3_md_rounds)
  	{
  		(void) slog(LG_DEBUG, "%s: rounds (%u) != default (%u)", MOWGLI_FUNC_NAME, rounds, crypt3_md_rounds);
  
  		*flags |= PWVERIFY_FLAG_RECRYPT;
  	}
  
  	return true;
  }
  
  static const struct crypt_impl crypto_crypt3_impl = {
  
  	.id        = CRYPTO_MODULE_NAME,
  	.crypt     = &atheme_crypt3_sha2_512_crypt,
  	.verify    = &atheme_crypt3_sha2_512_verify,
  };
  
  static void
  mod_init(struct module *const restrict m)
  {
  	MODULE_TRY_REQUEST_SYMBOL(m, crypto_conf_table, "crypto/main", "crypto_conf_table")
  
  	if (! atheme_crypt3_selftest(true, CRYPT3_MODULE_TEST_VECTOR_SHA2_512))
  	{
  		(void) slog(LG_ERROR, "%s: self-test failed, does this platform support this algorithm?", m->name);
  
  		m->mflags |= MODFLAG_FAIL;
  		return;
  	}
  
  	if (atheme_crypt3_selftest(false, CRYPT3_MODULE_TEST_VECTOR_SHA2_512_EXT))
  		(void) add_uint_conf_item("crypt3_sha2_512_rounds", *crypto_conf_table, 0, &crypt3_md_rounds,
  		                          CRYPT3_SHA2_ITERCNT_MIN, CRYPT3_SHA2_ITERCNT_MAX, CRYPT3_SHA2_ITERCNT_DEF);
  	else
  		(void) slog(LG_INFO, "%s: the number of rounds is not configurable on this platform", m->name);
  
  	(void) slog(LG_INFO, CRYPT3_MODULE_WARNING, m->name);
  
  	(void) crypt_register(&crypto_crypt3_impl);
  
  	m->mflags |= MODFLAG_DBCRYPTO;
  }
  
  static void
  mod_deinit(const enum module_unload_intent ATHEME_VATTR_UNUSED intent)
  {
  	(void) del_conf_item("crypt3_sha2_512_rounds", *crypto_conf_table);
  
  	(void) crypt_unregister(&crypto_crypt3_impl);
  }
  
  #else /* HAVE_CRYPT */
  
  static void
  mod_init(struct module *const restrict m)
  {
  	(void) slog(LG_ERROR, "Module %s requires crypt(3) support, refusing to load.", m->name);
  
  	m->mflags |= MODFLAG_FAIL;
  }
  
  static void
  mod_deinit(const enum module_unload_intent ATHEME_VATTR_UNUSED intent)
  {
  	// Nothing To Do
  }
  
  #endif /* !HAVE_CRYPT */
  
  SIMPLE_DECLARE_MODULE_V1(CRYPTO_MODULE_NAME, MODULE_UNLOAD_CAPABILITY_OK)
  