]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Implement support for Argon2 password hashing.
authorShutterQuick <shutter@canternet.org>
Sat, 10 Oct 2020 17:25:24 +0000 (19:25 +0200)
committerGitHub <noreply@github.com>
Sat, 10 Oct 2020 17:25:24 +0000 (18:25 +0100)
Resolves #1540.

.github/workflows/ci-linux.yml
.github/workflows/ci-macos.yml
.gitignore
configure
docs/conf/modules.conf.example
src/modules/extra/m_argon2.cpp [new file with mode: 0644]

index 0d7de47088159a5d9657a70f9134a245dd21f4b6..fa0d065e3c50190bcdbdff4c336b071cc8669541 100644 (file)
@@ -7,14 +7,14 @@ jobs:
     runs-on: ubuntu-16.04
     env:
       CXXFLAGS: -std=${{ matrix.standard }}
-      TEST_BUILD_MODULES: geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 regex_stdlib regex_tre sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal
+      TEST_BUILD_MODULES: argon2 geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 regex_stdlib regex_tre sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal
     steps:
       - uses: actions/checkout@v2
       - name: Install dependencies
         run: |
           sudo apt-get update --assume-yes
           sudo apt-get install --assume-yes --no-install-recommends clang g++ git make libc++-dev libc++abi-dev pkg-config
-          sudo apt-get install --assume-yes --no-install-recommends libgnutls28-dev libldap2-dev libmaxminddb-dev libmbedtls-dev libmysqlclient-dev libpcre3-dev libpq-dev libre2-dev libsqlite3-dev libssl-dev libtre-dev
+          sudo apt-get install --assume-yes --no-install-recommends libargon2-0-dev libgnutls28-dev libldap2-dev libmaxminddb-dev libmbedtls-dev libmysqlclient-dev libpcre3-dev libpq-dev libre2-dev libsqlite3-dev libssl-dev libtre-dev
       - name: Run test-build
         run: ./tools/test-build ${{ matrix.compiler }}
     strategy:
index db155781a6f801602424be0e48c6b238cc933e96..21296fc32ec0889fedcde99c5a1254406336050a 100644 (file)
@@ -9,13 +9,13 @@ jobs:
       CXXFLAGS: -std=${{ matrix.standard }} -I/usr/local/opt/openssl@1.1/include -Wno-error=deprecated-declarations
       LDFLAGS: -L/usr/local/opt/openssl@1.1/lib
       PKG_CONFIG_PATH: /usr/local/opt/openssl@1.1/lib/pkgconfig:/usr/local/opt/sqlite/lib/pkgconfig
-      TEST_BUILD_MODULES: geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 regex_stdlib regex_tre sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal
+      TEST_BUILD_MODULES: argon2 geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 regex_stdlib regex_tre sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal
     steps:
       - uses: actions/checkout@v2
       - name: Install dependencies
         run: |
           brew update
-          for PACKAGE in pkg-config gnutls libmaxminddb libpq mbedtls mysql-client openssl@1.1 pcre re2 sqlite tre;
+          for PACKAGE in pkg-config argon2 gnutls libmaxminddb libpq mbedtls mysql-client openssl@1.1 pcre re2 sqlite tre;
           do
             brew install $PACKAGE || brew upgrade $PACKAGE
           done
index 0bf719c7c9c735bb7615015949c8e3a1ad67d698..d5c7d4de87e81ca3d9abe8460589e39350da26ce 100644 (file)
@@ -14,6 +14,7 @@
 
 /include/config.h
 
+/src/modules/m_argon2.cpp
 /src/modules/m_geo_maxmind.cpp
 /src/modules/m_ldap.cpp
 /src/modules/m_mysql.cpp
index 767929a1d280df4b35638e3cb7edc8f6bba8c855..896ded1c40fd79dee2575fc1c468cbfd32d4a051 100755 (executable)
--- a/configure
+++ b/configure
@@ -363,6 +363,7 @@ if (prompt_bool $interactive, $question, 0) {
        # system './modulemanager', 'enable', '--auto';
        my %modules = (
                # Missing: m_ldap, m_regex_stdlib, m_ssl_mbedtls
+               'm_argon2.cpp'          => 'pkg-config --exists libargon2',
                'm_geo_maxmind.cpp'     => 'pkg-config --exists libmaxminddb',
                'm_mysql.cpp'           => 'mysql_config --version',
                'm_pgsql.cpp'           => 'pg_config --version',
index 3255360b93caa1badae133e6d05ee7a821fc95c5..2f74634d7abbf381c80b9cbb07ba6c7aa913b224 100644 (file)
 # <anticaps lowercase="abcdefghijklmnopqrstuvwxyz"
 #           uppercase="ABCDEFGHIJKLMNOPQRSTUVWXYZ">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Argon2 module: Allows other modules to generate Argon2 hashes,
+# usually for cryptographic uses and security.
+# This module makes the algorithms argon2i, argon2d and argon2id
+# available for use.
+# Note that this module is extra, and must be enabled explicitly
+# to build. It depends on libargon2.
+#<module name="argon2">
+#
+# memory: Memory hardness, in KiB. E.g. 131072 KiB = 128 MiB.
+# iterations: Time hardness in iterations. (def. 3)
+# lanes: How many parallel chains can be run. (def. 1)
+# threads: Maximum amount of threads each invokation can spawn. (def. 1)
+# length: Output length in bytes. (def. 32)
+# saltlength: Salt length in bytes. (def. 16)
+# version: Algorithm version, 10 or 13. (def. 13)
+# The parameters can be customized as follows:
+#<argon2 iterations="3" memory="131074" length="32" saltlength="16">
+# Defines the parameters that are common for all the variants (i/d/id).
+# Can be overriden on individual basis, e.g.
+#<argon2i iterations="4">
+#<argon2d memory="131074"
+#<argon2id iterations="5" memory="262144" length="64" saltlength="32">
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Auditorium module: Adds channel mode +u which makes everyone else
 # except you in the channel invisible, used for large meetings etc.
diff --git a/src/modules/extra/m_argon2.cpp b/src/modules/extra/m_argon2.cpp
new file mode 100644 (file)
index 0000000..8a4b2f3
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2020 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// $CompilerFlags: find_compiler_flags("libargon2" "")
+
+/// $LinkerFlags: find_linker_flags("libargon2" "-llibargon2")
+
+/// $PackageInfo: require_system("arch") argon2 pkgconf
+/// $PackageInfo: require_system("darwin") argon2 pkg-config
+/// $PackageInfo: require_system("debian" "9.0") libargon2-0 pkg-config
+/// $PackageInfo: require_system("ubuntu" "18.04") libargon2-0-dev pkg-config
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+#endif
+
+// Fix warnings about the use of `long long` on C++03
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
+#include <argon2.h>
+
+class ProviderConfig
+{
+ private:
+       static Argon2_version SanitizeArgon2Version(unsigned long version)
+       {
+               // Note, 10 is 0x10, and 13 is 0x13. Refering to it as
+               // dec 10 or 13 in the config file, for the name to
+               // match better.
+               switch (version)
+               {
+                       case 10:
+                               return ARGON2_VERSION_10;
+                       case 13:
+                               return ARGON2_VERSION_13;
+               }
+
+               ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Unknown Argon2 version (%lu) specified; assuming 13",
+                       version);
+               return ARGON2_VERSION_13;
+       }
+
+ public:
+       uint32_t iterations;
+       uint32_t lanes;
+       uint32_t memory;
+       uint32_t outlen;
+       uint32_t saltlen;
+       uint32_t threads;
+       Argon2_version version;
+
+       ProviderConfig()
+       {
+               // Nothing interesting happens here.
+       }
+
+       ProviderConfig(const std::string& tagname, ProviderConfig* def)
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue(tagname);
+
+               uint32_t def_iterations = def ? def->iterations : 3;
+               this->iterations = tag->getUInt("iterations", def_iterations, 1);
+
+               uint32_t def_lanes = def ? def->lanes : 1;
+               this->lanes = tag->getUInt("lanes", def_lanes, ARGON2_MIN_LANES, ARGON2_MAX_LANES);
+
+               uint32_t def_memory = def ? def->memory : 131072; // 128 MiB
+               this->memory = tag->getUInt("memory", def_memory, ARGON2_MIN_MEMORY, ARGON2_MAX_MEMORY);
+
+               uint32_t def_outlen = def ? def->outlen : 32;
+               this->outlen = tag->getUInt("length", def_outlen, ARGON2_MIN_OUTLEN, ARGON2_MAX_OUTLEN);
+
+               uint32_t def_saltlen = def ? def->saltlen : 16;
+               this->saltlen = tag->getUInt("saltlength", def_saltlen, ARGON2_MIN_SALT_LENGTH, ARGON2_MAX_SALT_LENGTH);
+
+               uint32_t def_threads = def ? def->threads : 1;
+               this->threads = tag->getUInt("threads", def_threads, ARGON2_MIN_THREADS, ARGON2_MAX_THREADS);
+
+               uint32_t def_version = def ? def->version : 13;
+               this->version = SanitizeArgon2Version(tag->getUInt("version", def_version));
+       }
+};
+
+class HashArgon2 : public HashProvider
+{
+ private:
+       const Argon2_type argon2Type;
+
+ public:
+       ProviderConfig config;
+
+       bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+       {
+               int result = argon2_verify(
+                       hash.c_str(),
+                       input.c_str(),
+                       input.length(),
+                       argon2Type);
+
+               return result == ARGON2_OK;
+       }
+
+       std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+       {
+               const std::string salt = ServerInstance->GenRandomStr(config.saltlen, false);
+
+               size_t encodedLen = argon2_encodedlen(
+                       config.iterations,
+                       config.memory,
+                       config.lanes,
+                       config.saltlen,
+                       config.outlen,
+                       argon2Type);
+
+               std::vector<char> raw_data(config.outlen);
+               std::vector<char> encoded_data(encodedLen + 1);
+
+               int argonResult = argon2_hash(
+                       config.iterations,
+                       config.memory,
+                       config.threads,
+                       data.c_str(),
+                       data.length(),
+                       salt.c_str(),
+                       salt.length(),
+                       &raw_data[0],
+                       raw_data.size(),
+                       &encoded_data[0],
+                       encoded_data.size(),
+                       argon2Type,
+                       config.version);
+
+               if (argonResult != ARGON2_OK)
+                       throw ModuleException("Argon2 hashing failed!: " + std::string(argon2_error_message(argonResult)));
+
+               // This isn't the raw version, but we don't have
+               // the facilities to juggle around the extra state required
+               // to do anything useful with them if we don't encode them.
+               // So we pretend this is the raw version, and instead make
+               // ToPrintable return its input.
+               return std::string(&encoded_data[0], encoded_data.size());
+       }
+
+       std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+       {
+               return raw;
+       }
+
+       HashArgon2(Module* parent, const std::string& hashName, Argon2_type type)
+               : HashProvider(parent, hashName)
+               , argon2Type(type)
+
+       {
+       }
+};
+
+class ModuleArgon2 : public Module
+{
+ private:
+       HashArgon2 argon2i;
+       HashArgon2 argon2d;
+       HashArgon2 argon2id;
+
+ public:
+       ModuleArgon2()
+               : argon2i(this, "argon2i", Argon2_i)
+               , argon2d(this, "argon2d", Argon2_d)
+               , argon2id(this, "argon2id", Argon2_id)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Allows other modules to generate Argon2 hashes.", VF_VENDOR);
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ProviderConfig defaultConfig("argon2", NULL);
+               argon2i.config = ProviderConfig("argon2i", &defaultConfig);
+               argon2d.config = ProviderConfig("argon2d", &defaultConfig);
+               argon2id.config = ProviderConfig("argon2id", &defaultConfig);
+       }
+};
+
+MODULE_INIT(ModuleArgon2)
+
+// This needs to be down here because of warnings from macros.
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif