bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi#include "test-lib.h"
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi#include "password-scheme.h"
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi#ifdef HAVE_LIBSODIUM
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi#include <sodium.h>
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi#endif
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomistatic struct {
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *scheme_generated;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *scheme_detected;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi} known_non_aliases[] = {
cfb22f2f9b28d5888ba00ad910e47c9a490ca673Aki Tuomi { "MD5", "DES-CRYPT" },
cfb22f2f9b28d5888ba00ad910e47c9a490ca673Aki Tuomi { "MD5-CRYPT", "DES-CRYPT" },
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi { "SKEY", "OTP" },
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi { "ARGON2ID", "ARGON2I" },
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi};
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi/* some algorithms are detected as something other, because they are compatible
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi but not considered aliases by dovecot. treat those here to avoid false errors. */
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomistatic bool schemes_are_known_non_alias(const char *generated, const char *detected)
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi{
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi for(size_t i = 0; i < N_ELEMENTS(known_non_aliases); i++) {
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi if (strcmp(known_non_aliases[i].scheme_generated, generated) == 0 &&
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi strcmp(known_non_aliases[i].scheme_detected, detected) == 0)
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi return TRUE;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi }
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi return FALSE;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi}
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomistatic void
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomitest_password_scheme(const char *scheme, const char *crypted,
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *plaintext)
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi{
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi struct password_generate_params params = {
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi .user = "testuser1",
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi .rounds = 0,
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi };
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const unsigned char *raw_password;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi size_t siz;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *error, *scheme2;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_begin(t_strdup_printf("password scheme(%s)", scheme));
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(strcmp(password_get_scheme(&crypted), scheme) == 0);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_decode(crypted, scheme, &raw_password, &siz, &error) == 1);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_verify(plaintext, &params, scheme, raw_password, siz, &error) == 1);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_generate_encoded(plaintext, &params, scheme, &crypted));
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi crypted = t_strdup_printf("{%s}%s", scheme, crypted);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(strcmp(password_get_scheme(&crypted), scheme) == 0);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_decode(crypted, scheme, &raw_password, &siz, &error) == 1);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_verify(plaintext, &params, scheme, raw_password, siz, &error) == 1);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi scheme2 = password_scheme_detect(plaintext, crypted, &params);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(scheme2 != NULL &&
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi (password_scheme_is_alias(scheme, scheme2) ||
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi schemes_are_known_non_alias(scheme, scheme2)));
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_end();
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi}
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomistatic void test_password_failures(void)
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi{
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *scheme = "PLAIN";
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *crypted = "{PLAIN}invalid";
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *plaintext = "test";
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi struct password_generate_params params = {
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi .user = "testuser1",
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi .rounds = 0,
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi };
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const unsigned char *raw_password;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi size_t siz;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi const char *error;
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_begin("password scheme failures");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi /* wrong password */
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(strcmp(password_get_scheme(&crypted), scheme) == 0);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_decode(crypted, scheme, &raw_password, &siz, &error) == 1);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_verify(plaintext, &params, scheme, raw_password, siz, &error) == 0);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi /* unknown scheme */
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi crypted = "{INVALID}invalid";
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi scheme = password_get_scheme(&crypted);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_decode(crypted, scheme, &raw_password, &siz, &error) == 0);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi /* crypt with empty value */
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_assert(password_verify(plaintext, &params, "CRYPT", NULL, 0, &error) == 0);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_end();
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi}
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomistatic void test_password_schemes(void)
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi{
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("PLAIN", "{PLAIN}test", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("CRYPT", "{CRYPT}//EsnG9FLTKjo", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("PLAIN-MD4", "{PLAIN-MD4}db346d691d7acc4dc2625db19f9e3f52", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("MD5", "{MD5}$1$wmyrgRuV$kImF6.9MAFQNHe23kq5vI/", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SHA1", "{SHA1}qUqP5cyxm6YcTAhz05Hph5gvu9M=", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("LANMAN", "{LANMAN}01fc5a6be7bc6929aad3b435b51404ee", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("NTLM", "{NTLM}0cb6948805f797bf2a82807973b89537", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SMD5", "{SMD5}JTu1KRwptKZJg/RLd+6Vn5GUd0M=", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("LDAP-MD5", "{LDAP-MD5}CY9rzUYh03PK3k6DJie09g==", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SHA256", "{SHA256}n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SHA512", "{SHA512}7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SSHA", "{SSHA}H/zrDv8FXUu1JmwvVYijfrYEF34jVZcO", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("MD5-CRYPT", "{MD5-CRYPT}$1$GgvxyNz8$OjZhLh4P.gF1lxYEbLZ3e/", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("OTP", "{OTP}sha1 1024 ae6b49aa481f7233 f69fc7f98b8fbf54", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("PBKDF2", "{PBKDF2}$1$bUnT4Pl7yFtYX0KU$5000$50a83cafdc517b9f46519415e53c6a858908680a", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("RPA", "{RPA}f89cb77d46507afe985d80822b6b6c39", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SKEY", "{SKEY}md4 1024 ce20d20fae368ff2 689aea1b24ed6438", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("CRAM-MD5", "{CRAM-MD5}e02d374fde0dc75a17a557039a3a5338c7743304777dccd376f332bee68d2cf6", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("DIGEST-MD5", "{DIGEST-MD5}77c1a8c437c9b08ba2f460fe5d58db5d", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("SCRAM-SHA-1", "{SCRAM-SHA-1}4096,GetyLXdBuHzf1FWf8SLz2Q==,NA/OqmF4hhrsrB9KR7po+dliTGM=,QBiURvQaE6H6qYTmeghDHLANBFQ=", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_scheme("BLF-CRYPT", "{BLF-CRYPT}$2y$05$11ipvo5dR6CwkzwmhwM26OXgzXwhV2PyPuLV.Qi31ILcRcThQpEiW", "test");
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi#ifdef HAVE_LIBSODIUM
2437015089814f398b717725915009878b3f45aaAki Tuomi test_password_scheme("ARGON2I", "{ARGON2I}$argon2i$v=19$m=32768,t=4,p=1$f2iuP4aUeNMrgu34fhOkkg$1XSZZMWlIs0zmE+snlUIcLADO3GXbA2O/hsQmmc317k", "test");
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi#ifdef crypto_pwhash_ALG_ARGON2ID13
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi test_password_scheme("ARGON2ID", "{ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$vBb99oJ12p3WAdYlaMHz1A$jtFOtbo/sYV9OSlTxDo/nVNq3uArHd5GJSEx0ty85Cc", "test");
66db268d57e782c952c8c0e5b1127ee6b69aecebAki Tuomi#endif
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi#endif
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi}
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomiint main(void)
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi{
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi static void (*const test_functions[])(void) = {
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_schemes,
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi test_password_failures,
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi NULL
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi };
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi password_schemes_init();
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi return test_run(test_functions);
32c2612514a404ebc226f32bb88f28d76ceb1db1Aki Tuomi}