/* Module for providing SHA-2 hashing * * (C) 2003-2025 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; } 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) { sha224provider.Check({ { "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "" }, { "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525", "The quick brown fox jumps over the lazy dog" }, }); sha256provider.Check({ { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "" }, { "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", "The quick brown fox jumps over the lazy dog" }, }); sha384provider.Check({ { "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", "" }, { "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1", "The quick brown fox jumps over the lazy dog" }, }); sha512provider.Check({ { "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "" }, { "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", "The quick brown fox jumps over the lazy dog" }, }); } void OnReload(Configuration::Conf &conf) override { this->defaultprovider = GetAlgorithm(Config->GetModule(this).Get("algorithm", "sha512")); } EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override { if (!defaultprovider) return EVENT_CONTINUE; auto key = GenerateKey(defaultprovider->digest_size); auto hmac = defaultprovider->HMAC(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); bool is_hmac = !hash_method.compare(0, 5, "hmac-", 5); if (!is_hmac && hash_method.compare(0, 4, "raw-", 4)) return; // Not a SHA-2 password. auto *provider = GetAlgorithm(hash_method.substr(is_hmac ? 5 : 4)); if (!provider) return; // Not a hash for this module. auto valid = false; if (is_hmac) { auto bpos = nc->pass.find(':', apos + 1); if (bpos == Anope::string::npos) return; // No HMAC key. // If you are using PHP you can validate passwords like this: // // 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(provider->HMAC(key, req->GetPassword())); valid = pass_hex.equals_cs(enc); } else { Anope::string pass_hex(nc->pass.begin() + apos + 1, nc->pass.end()); valid = provider->Compare(pass_hex, req->GetPassword()); } if (valid) { // If we are NOT the first encryption module, the password is a raw // hash, or the algorithm is different we want to re-encrypt the // password with the primary encryption method. if (ModuleManager::FindFirstOf(ENCRYPTION) != this || !is_hmac || provider != defaultprovider) Anope::Encrypt(req->GetPassword(), nc->pass); req->Success(this); } } }; MODULE_INIT(ESHA2)