2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
6 * This file is part of InspIRCd. InspIRCd is free software: you can
7 * redistribute it and/or modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation, version 2.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "modules/hash.h"
24 // Iterations:B64(Hash):B64(Salt)
26 // 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
30 unsigned int iterations;
35 PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
36 : iterations(itr), length(dkl), salt(slt), hash(hsh)
40 PBKDF2Hash(const std::string& data)
42 irc::sepstream ss(data, ':');
46 this->iterations = ConvToNum<unsigned int>(tok);
49 this->hash = Base64ToBin(tok);
52 this->salt = Base64ToBin(tok);
54 this->length = this->hash.length();
57 std::string ToString()
61 return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
66 if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
72 class PBKDF2Provider : public HashProvider
75 HashProvider* provider;
76 unsigned int iterations;
77 unsigned int dkey_length;
79 std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
81 size_t blocks = std::ceil((double)dkl / provider->out_size);
85 std::string salt_block = salt;
86 for (size_t block = 1; block <= blocks; block++)
89 for (size_t i = 0; i < sizeof(salt_data); i++)
90 salt_data[i] = block >> (24 - i * 8) & 0x0F;
92 salt_block.erase(salt.length());
93 salt_block.append(salt_data, sizeof(salt_data));
95 std::string blockdata = provider->hmac(pass, salt_block);
96 std::string lasthash = blockdata;
97 for (size_t iter = 1; iter < itr; iter++)
99 tmphash = provider->hmac(pass, lasthash);
100 for (size_t i = 0; i < provider->out_size; i++)
101 blockdata[i] ^= tmphash[i];
103 lasthash.swap(tmphash);
112 std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
114 PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
115 hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
116 return hs.ToString();
119 bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
125 std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
126 return (cmp == hs.hash);
129 std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
134 PBKDF2Provider(Module* mod, HashProvider* hp)
135 : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
138 DisableAutoRegister();
142 struct ProviderConfig
144 unsigned long dkey_length;
145 unsigned long iterations;
148 typedef std::map<std::string, ProviderConfig> ProviderConfigMap;
150 class ModulePBKDF2 : public Module
152 std::vector<PBKDF2Provider*> providers;
153 ProviderConfig globalconfig;
154 ProviderConfigMap providerconfigs;
156 ProviderConfig GetConfigForProvider(const std::string& name) const
158 ProviderConfigMap::const_iterator it = providerconfigs.find(name);
159 if (it == providerconfigs.end())
165 void ConfigureProviders()
167 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
169 PBKDF2Provider* pi = *i;
170 ProviderConfig config = GetConfigForProvider(pi->name);
171 pi->iterations = config.iterations;
172 pi->dkey_length = config.dkey_length;
178 // First set the common values
179 ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
180 ProviderConfig newglobal;
181 newglobal.iterations = tag->getUInt("iterations", 12288, 1);
182 newglobal.dkey_length = tag->getUInt("length", 32, 1, 1024);
184 // Then the specific values
185 ProviderConfigMap newconfigs;
186 ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
187 for (ConfigIter i = tags.first; i != tags.second; ++i)
190 std::string hash_name = "hash/" + tag->getString("hash");
191 ProviderConfig& config = newconfigs[hash_name];
193 config.iterations = tag->getUInt("iterations", newglobal.iterations, 1);
194 config.dkey_length = tag->getUInt("length", newglobal.dkey_length, 1, 1024);
197 // Config is valid, apply it
198 providerconfigs.swap(newconfigs);
199 std::swap(globalconfig, newglobal);
200 ConfigureProviders();
206 stdalgo::delete_all(providers);
209 void OnServiceAdd(ServiceProvider& provider) CXX11_OVERRIDE
211 // Check if it's a hash provider
212 if (provider.name.compare(0, 5, "hash/"))
215 HashProvider* hp = static_cast<HashProvider*>(&provider);
219 PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
220 providers.push_back(prov);
221 ServerInstance->Modules.AddService(*prov);
223 ConfigureProviders();
226 void OnServiceDel(ServiceProvider& prov) CXX11_OVERRIDE
228 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
230 PBKDF2Provider* item = *i;
231 if (item->provider != &prov)
234 ServerInstance->Modules->DelService(*item);
241 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
246 Version GetVersion() CXX11_OVERRIDE
248 return Version("Implements PBKDF2 hashing", VF_VENDOR);
252 MODULE_INIT(ModulePBKDF2)