From 6e0f0b8896deb71cc112d7eadc10eedcc3081cba Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Sat, 9 Mar 2024 16:41:26 +0000 Subject: Add the enc_sha2 module which hashes passwords with HMAC-SHA-2. Using HMAC instead of changing the IV is a lot safer. --- modules/encryption/enc_sha2.cpp | 190 ++++++++++++++++++++++++++++++++++++++ modules/encryption/enc_sha256.cpp | 11 +-- 2 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 modules/encryption/enc_sha2.cpp (limited to 'modules/encryption') diff --git a/modules/encryption/enc_sha2.cpp b/modules/encryption/enc_sha2.cpp new file mode 100644 index 000000000..d625bf949 --- /dev/null +++ b/modules/encryption/enc_sha2.cpp @@ -0,0 +1,190 @@ +/* Module for providing bcrypt hashing + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * This program is free but copyrighted software; see the file COPYING for + * details. + * + */ + +#include +#include + +#include "sha2/sha2.c" + +#include "module.h" +#include "modules/encryption.h" + +template +class SHA2Context final + : public Encryption::Context +{ +private: + SHAContext context; + const size_t digest_size; + +public: + SHA2Context(size_t ds) + : digest_size(ds) + { + SHAInit(&context); + } + + void Update(const unsigned char *data, size_t len) override + { + SHAUpdate(&context, data, len); + } + + Anope::string Finalize() override + { + std::vector digest(digest_size); + SHAFinal(&context, digest.data()); + return Anope::string(reinterpret_cast(digest.data()), digest.size()); + } +}; + +template +class SHA2Provider final + : public Encryption::Provider +{ +public: + SHA2Provider(Module *creator, const Anope::string &algorithm, size_t bs, size_t ds) + : Encryption::Provider(creator, algorithm, bs, ds) + { + } + + std::unique_ptr CreateContext() override + { + return std::make_unique>(this->digest_size); + } +}; + +class ESHA2 final + : public Module +{ +private: + Encryption::Provider *defaultprovider = nullptr; + SHA2Provider sha224provider; + SHA2Provider sha256provider; + SHA2Provider sha384provider; + SHA2Provider sha512provider; + + Anope::string GenerateKey(size_t keylen) + { + static std::random_device device; + static std::mt19937 engine(device()); + static std::uniform_int_distribution dist(CHAR_MIN, CHAR_MAX); + Anope::string keybuf(keylen, ' '); + for (size_t i = 0; i < keylen; ++i) + keybuf[i] = static_cast(dist(engine)); + return keybuf; + } + + Encryption::Provider *GetAlgorithm(const Anope::string &algorithm) + { + if (algorithm == "sha224") + return &sha224provider; + if (algorithm == "sha256") + return &sha256provider; + if (algorithm == "sha384") + return &sha384provider; + if (algorithm == "sha512") + return &sha512provider; + return nullptr; + } + + Anope::string HMAC(Encryption::Provider *provider, const Anope::string &key, const Anope::string &data) + { + auto keybuf = key.length() > provider->block_size ? provider->Encrypt(key) : key; + keybuf.resize(provider->block_size); + + Anope::string hmac1; + Anope::string hmac2; + for (size_t i = 0; i < provider->block_size; ++i) + { + hmac1.push_back(static_cast(keybuf[i] ^ 0x5C)); + hmac2.push_back(static_cast(keybuf[i] ^ 0x36)); + } + hmac2.append(data); + hmac1.append(provider->Encrypt(hmac2)); + + return provider->Encrypt(hmac1); + } + +public: + ESHA2(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, ENCRYPTION | VENDOR) + , sha224provider(this, "sha224", SHA224_BLOCK_SIZE, SHA224_DIGEST_SIZE) + , sha256provider(this, "sha256", SHA256_BLOCK_SIZE, SHA256_DIGEST_SIZE) + , sha384provider(this, "sha384", SHA384_BLOCK_SIZE, SHA384_DIGEST_SIZE) + , sha512provider(this, "sha512", SHA512_BLOCK_SIZE, SHA512_DIGEST_SIZE) + { + } + + void OnReload(Configuration::Conf *conf) override + { + this->defaultprovider = GetAlgorithm(Config->GetModule(this)->Get("algorithm", "sha256")); + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override + { + if (!defaultprovider) + return EVENT_CONTINUE; + + auto key = GenerateKey(defaultprovider->digest_size); + auto hmac = HMAC(defaultprovider, key, src); + auto enc = "hmac-" + defaultprovider->name + ":" + Anope::Hex(hmac) + ":" + Anope::Hex(key); + Log(LOG_DEBUG_2) << "(enc_sha2) hashed password from [" << src << "] to [" << enc << "]"; + dest = enc; + return EVENT_ALLOW; + } + + void OnCheckAuthentication(User *, IdentifyRequest *req) override + { + const auto *na = NickAlias::Find(req->GetAccount()); + if (!na) + return; + + NickCore *nc = na->nc; + auto apos = nc->pass.find(':'); + if (apos == Anope::string::npos) + return; + + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + apos); + if (hash_method.compare(0, 5, "hmac-", 5)) + return; // Not a HMAC hash. + + auto provider = GetAlgorithm(hash_method.substr(5)); + if (!provider) + return; // Not a hash for this module. + + auto bpos = nc->pass.find(':', apos + 1); + if (bpos == Anope::string::npos) + return; // No HMAC key. + + Anope::string pass_hex(nc->pass.begin() + apos + 1, nc->pass.begin() + bpos); + Anope::string key_hex(nc->pass.begin() + bpos + 1, nc->pass.end()); + Anope::string key; + Anope::Unhex(key_hex, key); + + auto enc = Anope::Hex(HMAC(provider, key, req->GetPassword())); + if (pass_hex.equals_cs(enc)) + { + // If we are NOT the first encryption module or the algorithm is + // different we want to re-encrypt the password with the primary + // encryption method. + if (ModuleManager::FindFirstOf(ENCRYPTION) != this || provider != defaultprovider) + Anope::Encrypt(req->GetPassword(), nc->pass); + req->Success(this); + } + } +}; + +MODULE_INIT(ESHA2) diff --git a/modules/encryption/enc_sha256.cpp b/modules/encryption/enc_sha256.cpp index 506c07328..0b883dc50 100644 --- a/modules/encryption/enc_sha256.cpp +++ b/modules/encryption/enc_sha256.cpp @@ -48,7 +48,6 @@ */ #include "module.h" -#include "modules/encryption.h" static const unsigned SHA256_DIGEST_SIZE = 256 / 8; static const unsigned SHA256_BLOCK_SIZE = 512 / 8; @@ -112,7 +111,6 @@ static const uint32_t sha256_k[64] = /** An sha256 context */ class SHA256Context final - : public Encryption::Context { void Transform(unsigned char *message, unsigned block_nb) { @@ -169,7 +167,7 @@ public: this->h[i] = iv[i]; } - void Update(const unsigned char *message, size_t mlen) override + void Update(const unsigned char *message, size_t mlen) { unsigned tmp_len = SHA256_BLOCK_SIZE - this->len, rem_len = mlen < tmp_len ? mlen : tmp_len; @@ -191,7 +189,7 @@ public: this->tot_len += (block_nb + 1) << 6; } - Anope::string Finalize() override + Anope::string Finalize() { unsigned block_nb = 1 + ((SHA256_BLOCK_SIZE - 9) < (this->len % SHA256_BLOCK_SIZE)); unsigned len_b = (this->tot_len + this->len) << 3; @@ -213,8 +211,6 @@ class ESHA256 final : public Module { private: - Encryption::SimpleProvider sha256provider; - unsigned iv[8]; bool use_iv; @@ -250,9 +246,10 @@ private: public: ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR) - , sha256provider(this, "sha256", SHA256_BLOCK_SIZE, SHA256_DIGEST_SIZE) { use_iv = false; + if (ModuleManager::FindFirstOf(ENCRYPTION) == this) + throw ModuleException("enc_sha256 is deprecated and can not be used as a primary encryption method"); } EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override -- cgit