summaryrefslogtreecommitdiff
path: root/include/modules/encryption.h
blob: c475f4fbddfb5dd98edd58c26b857a7701b2255d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 *
 * (C) 2003-2024 Anope Team
 * Contact us at team@anope.org
 *
 * Please read COPYING and README for further details.
 *
 * Based on the original code of Epona by Lara.
 * Based on the original code of Services by Andy Church.
 */

#pragma once

namespace Encryption
{
	/** Base class for encryption contexts. */
	class Context
	{
	public:
		virtual ~Context() = default;

		/** Updates the encryption context with the specified data.
		 * @param str The data to update the context with.
		 */
		inline void Update(const Anope::string &str)
		{
			Update(reinterpret_cast<const unsigned char *>(str.c_str()), str.length());
		}

		/** Updates the encryption context with the specified data.
		 * @param data The data to update the context with.
		 * @param len The length of the data.
		 */
		virtual void Update(const unsigned char *data, size_t len) = 0;

		/** Finalises the encryption context and returns the digest. */
		virtual Anope::string Finalize() = 0;
	};

	/** Provider of encryption contexts. */
	class Provider
		: public Service
	{
	public:
		/** The byte size of the block cipher. */
		const size_t block_size;

		/** The byte size of the resulting digest. */
		const size_t digest_size;

		/** Creates a provider of encryption contexts.
		 * @param creator The module that created this provider.
		 * @param algorithm The name of the encryption algorithm.
		 * @param bs The byte size of the block cipher.
		 * @param ds The byte size of the resulting digest.
		 */
		Provider(Module *creator, const Anope::string &algorithm, size_t bs, size_t ds)
			: Service(creator, "Encryption::Provider", algorithm)
			, block_size(bs)
			, digest_size(ds)
		{
		}

		virtual ~Provider() = default;

		/** Checks whether a plain text value matches a hash created by this provider. */
		virtual bool Compare(const Anope::string &hash, const Anope::string &plain)
		{
			return !hash.empty() && hash.equals_cs(ToPrintable(Encrypt(plain)));
		}

		/** Called on initialising a encryption provider to check it works properly. */
		void Check(const Anope::map<Anope::string> &checks)
		{
			for (const auto &[hash, plain] : checks)
			{
				if (!Compare(hash, plain))
					throw ModuleException("BUG: unable to generate " + this->name + " hashes safely! Please report this!");
			}
			Log(LOG_DEBUG) << "The " << this->name << " encryption provider appears to be working correctly.";
		}

		/** Creates a new encryption context. */
		virtual std::unique_ptr<Context> CreateContext() = 0;

		/** Quickly encrypts the specified values and returns the digest. */
		template<typename... Args>
		Anope::string Encrypt(Args &&...args)
		{
			auto context = CreateContext();
			context->Update(std::forward<Args>(args)...);
			return context->Finalize();
		}

		/** Calculates the  RFC 2104 hash-based message authentication code for the specified data. */
		inline Anope::string HMAC(const Anope::string &key, const Anope::string &data)
		{
			if (!block_size)
				return {};

			auto keybuf = key.length() > block_size ? Encrypt(key) : key;
			keybuf.resize(block_size);

			Anope::string hmac1;
			Anope::string hmac2;
			for (size_t i = 0; i < 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(Encrypt(hmac2));

			return Encrypt(hmac1);
		}

		/** Converts a hash to its printable form. */
		virtual Anope::string ToPrintable(const Anope::string &hash)
		{
			return Anope::Hex(hash);
		}
	};

	/** Helper template for creating simple providers of encryption contexts. */
	template <typename T>
	class SimpleProvider final
		: public Provider
	{
	public:
		/** Creates a simple provider of encryption contexts.
		 * @param creator The module that created this provider.
		 * @param algorithm The name of the encryption algorithm.
		 * @param bs The byte size of the block cipher.
		 * @param ds The byte size of the resulting digest.
		 */
		SimpleProvider(Module *creator, const Anope::string &algorithm, size_t bs, size_t ds)
			: Provider(creator, algorithm, bs, ds)
		{
		}

		/** @copydoc Encryption::Provider::CreateContext. */
		std::unique_ptr<Context> CreateContext() override
		{
			return std::make_unique<T>();
		}
	};
}