summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2024-03-09 16:41:26 +0000
committerSadie Powell <sadie@witchery.services>2024-03-09 22:22:56 +0000
commit6e0f0b8896deb71cc112d7eadc10eedcc3081cba (patch)
treeae3787cc8bad1354836669431acb4c05c3b3cf26 /modules
parent2c5b2c649ef59be1695132d02377312cac523497 (diff)
Add the enc_sha2 module which hashes passwords with HMAC-SHA-2.
Using HMAC instead of changing the IV is a lot safer.
Diffstat (limited to 'modules')
-rw-r--r--modules/encryption/enc_sha2.cpp190
-rw-r--r--modules/encryption/enc_sha256.cpp11
2 files changed, 194 insertions, 7 deletions
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 <climits>
+#include <random>
+
+#include "sha2/sha2.c"
+
+#include "module.h"
+#include "modules/encryption.h"
+
+template <typename SHAContext,
+ void (* SHAInit)(SHAContext *),
+ void (* SHAUpdate)(SHAContext *, const unsigned char *, unsigned int),
+ void (* SHAFinal)(SHAContext *, unsigned char *)>
+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<unsigned char> digest(digest_size);
+ SHAFinal(&context, digest.data());
+ return Anope::string(reinterpret_cast<const char *>(digest.data()), digest.size());
+ }
+};
+
+template <typename SHAContext,
+ void (* SHAInit)(SHAContext *),
+ void (* SHAUpdate)(SHAContext *, const unsigned char *, unsigned int),
+ void (* SHAFinal)(SHAContext *, unsigned char *)>
+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<Encryption::Context> CreateContext() override
+ {
+ return std::make_unique<SHA2Context<SHAContext, SHAInit, SHAUpdate, SHAFinal>>(this->digest_size);
+ }
+};
+
+class ESHA2 final
+ : public Module
+{
+private:
+ Encryption::Provider *defaultprovider = nullptr;
+ SHA2Provider<sha224_ctx, sha224_init, sha224_update, sha224_final> sha224provider;
+ SHA2Provider<sha256_ctx, sha256_init, sha256_update, sha256_final> sha256provider;
+ SHA2Provider<sha384_ctx, sha384_init, sha384_update, sha384_final> sha384provider;
+ SHA2Provider<sha512_ctx, sha512_init, sha512_update, sha512_final> sha512provider;
+
+ Anope::string GenerateKey(size_t keylen)
+ {
+ static std::random_device device;
+ static std::mt19937 engine(device());
+ static std::uniform_int_distribution<int> dist(CHAR_MIN, CHAR_MAX);
+ Anope::string keybuf(keylen, ' ');
+ for (size_t i = 0; i < keylen; ++i)
+ keybuf[i] = static_cast<char>(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<char>(keybuf[i] ^ 0x5C));
+ hmac2.push_back(static_cast<char>(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<const Anope::string>("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<SHA256Context> 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