02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include "lib.h"
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen#include "password-scheme.h"
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#ifdef HAVE_LIBSODIUM
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen#include <sodium.h>
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenstatic void
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainengenerate_argon2i(const char *plaintext, const struct password_generate_params *params,
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen const unsigned char **raw_password_r, size_t *size_r)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen{
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen unsigned long long rounds = params->rounds;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen size_t memlimit;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen char result[crypto_pwhash_STRBYTES];
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen if (rounds == 0)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen rounds = crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen if (rounds >= crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen memlimit = crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen else if (rounds >= crypto_pwhash_argon2i_OPSLIMIT_MODERATE)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen memlimit = crypto_pwhash_argon2i_MEMLIMIT_MODERATE;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen else
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen memlimit = crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen if (crypto_pwhash_argon2i_str(result, plaintext, strlen(plaintext), rounds, memlimit) < 0)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen i_fatal("crypto_pwhash_argon2i_str failed: %m");
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen *raw_password_r = (const unsigned char*)t_strdup(result);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *size_r = strlen(result);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#ifdef crypto_pwhash_ALG_ARGON2ID13
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstatic void
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainengenerate_argon2id(const char *plaintext, const struct password_generate_params *params,
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen const unsigned char **raw_password_r, size_t *size_r)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen{
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen unsigned long long rounds = params->rounds;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen size_t memlimit;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen char result[crypto_pwhash_argon2id_STRBYTES];
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_zero(result);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen if (rounds == 0)
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen rounds = crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE;
e0ba54c7f985fc403b41c6e36d6a7f44908b23f0Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (rounds >= crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen memlimit = crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen else if (rounds >= crypto_pwhash_argon2id_OPSLIMIT_MODERATE)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen memlimit = crypto_pwhash_argon2id_MEMLIMIT_MODERATE;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen else
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen memlimit = crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* XXX: Bug in sodium-1.0.13, it expects rounds to be 3 */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (rounds < 3)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen rounds = 3;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (crypto_pwhash_argon2id_str(result, plaintext, strlen(plaintext), rounds, memlimit) < 0)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_fatal("crypto_pwhash_argon2id_str failed: %m");
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *raw_password_r = (const unsigned char*)t_strdup(result);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *size_r = strlen(result);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#endif
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainenstatic int
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainenverify_argon2(const char *plaintext, const struct password_generate_params *params ATTR_UNUSED,
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen const unsigned char *raw_password, size_t size,
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen const char **error_r ATTR_UNUSED)
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen{
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen const char *passwd = t_strndup(raw_password, size);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (crypto_pwhash_str_verify(passwd, plaintext, strlen(plaintext)) < 0)
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen return 0;
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen return 1;
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen}
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainen
4bfa47e475c957adfc645047660d8ce96a3371a7Timo Sirainenstatic const struct password_scheme sodium_schemes[] = {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen { "ARGON2I", PW_ENCODING_NONE, 0, verify_argon2,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen generate_argon2i },
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#ifdef crypto_pwhash_ALG_ARGON2ID13
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen { "ARGON2ID", PW_ENCODING_NONE, 0, verify_argon2,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen generate_argon2id },
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#endif
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen};
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenvoid password_scheme_register_sodium(void)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen{
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (sodium_init() != 0)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_fatal("sodium_init() failed");
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen for(size_t i = 0; i < N_ELEMENTS(sodium_schemes); i++)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen password_scheme_register(&sodium_schemes[i]);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen}
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#endif
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen