2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
5 * Copyright (C) 2018 Sadie Powell <sadie@witchery.services>
6 * Copyright (C) 2014, 2016 Attila Molnar <attilamolnar@hush.com>
7 * Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
9 * This file is part of InspIRCd. InspIRCd is free software: you can
10 * redistribute it and/or modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation, version 2.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "modules/hash.h"
27 // Iterations:B64(Hash):B64(Salt)
29 // 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
33 unsigned int iterations;
38 PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
39 : iterations(itr), length(dkl), salt(slt), hash(hsh)
43 PBKDF2Hash(const std::string& data)
45 irc::sepstream ss(data, ':');
49 this->iterations = ConvToNum<unsigned int>(tok);
52 this->hash = Base64ToBin(tok);
55 this->salt = Base64ToBin(tok);
57 this->length = this->hash.length();
60 std::string ToString()
64 return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
69 if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
75 class PBKDF2Provider : public HashProvider
78 HashProvider* provider;
79 unsigned int iterations;
80 unsigned int dkey_length;
82 std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
84 size_t blocks = std::ceil((double)dkl / provider->out_size);
88 std::string salt_block = salt;
89 for (size_t block = 1; block <= blocks; block++)
92 for (size_t i = 0; i < sizeof(salt_data); i++)
93 salt_data[i] = block >> (24 - i * 8) & 0x0F;
95 salt_block.erase(salt.length());
96 salt_block.append(salt_data, sizeof(salt_data));
98 std::string blockdata = provider->hmac(pass, salt_block);
99 std::string lasthash = blockdata;
100 for (size_t iter = 1; iter < itr; iter++)
102 tmphash = provider->hmac(pass, lasthash);
103 for (size_t i = 0; i < provider->out_size; i++)
104 blockdata[i] ^= tmphash[i];
106 lasthash.swap(tmphash);
115 std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
117 PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
118 hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
119 return hs.ToString();
122 bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
128 std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
129 return InspIRCd::TimingSafeCompare(cmp, hs.hash);
132 std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
137 PBKDF2Provider(Module* mod, HashProvider* hp)
138 : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
141 DisableAutoRegister();
145 struct ProviderConfig
147 unsigned long dkey_length;
148 unsigned long iterations;
151 typedef std::map<std::string, ProviderConfig> ProviderConfigMap;
153 class ModulePBKDF2 : public Module
155 std::vector<PBKDF2Provider*> providers;
156 ProviderConfig globalconfig;
157 ProviderConfigMap providerconfigs;
159 ProviderConfig GetConfigForProvider(const std::string& name) const
161 ProviderConfigMap::const_iterator it = providerconfigs.find(name);
162 if (it == providerconfigs.end())
168 void ConfigureProviders()
170 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
172 PBKDF2Provider* pi = *i;
173 ProviderConfig config = GetConfigForProvider(pi->name);
174 pi->iterations = config.iterations;
175 pi->dkey_length = config.dkey_length;
181 // First set the common values
182 ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
183 ProviderConfig newglobal;
184 newglobal.iterations = tag->getUInt("iterations", 12288, 1);
185 newglobal.dkey_length = tag->getUInt("length", 32, 1, 1024);
187 // Then the specific values
188 ProviderConfigMap newconfigs;
189 ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
190 for (ConfigIter i = tags.first; i != tags.second; ++i)
193 std::string hash_name = "hash/" + tag->getString("hash");
194 ProviderConfig& config = newconfigs[hash_name];
196 config.iterations = tag->getUInt("iterations", newglobal.iterations, 1);
197 config.dkey_length = tag->getUInt("length", newglobal.dkey_length, 1, 1024);
200 // Config is valid, apply it
201 providerconfigs.swap(newconfigs);
202 std::swap(globalconfig, newglobal);
203 ConfigureProviders();
209 stdalgo::delete_all(providers);
212 void OnServiceAdd(ServiceProvider& provider) CXX11_OVERRIDE
214 // Check if it's a hash provider
215 if (provider.name.compare(0, 5, "hash/"))
218 HashProvider* hp = static_cast<HashProvider*>(&provider);
222 PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
223 providers.push_back(prov);
224 ServerInstance->Modules.AddService(*prov);
226 ConfigureProviders();
229 void OnServiceDel(ServiceProvider& prov) CXX11_OVERRIDE
231 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
233 PBKDF2Provider* item = *i;
234 if (item->provider != &prov)
237 ServerInstance->Modules->DelService(*item);
244 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
249 Version GetVersion() CXX11_OVERRIDE
251 return Version("Allows other modules to generate PBKDF2 hashes.", VF_VENDOR);
255 MODULE_INIT(ModulePBKDF2)