]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_pbkdf2.cpp
Merge insp20
[user/henk/code/inspircd.git] / src / modules / m_pbkdf2.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
5  *
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.
9  *
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
13  * details.
14  *
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/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "modules/hash.h"
22
23 // Format:
24 // Iterations:B64(Hash):B64(Salt)
25 // E.g.
26 // 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
27 class PBKDF2Hash
28 {
29  public:
30         unsigned int iterations;
31         unsigned int length;
32         std::string salt;
33         std::string hash;
34
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)
37         {
38         }
39
40         PBKDF2Hash(const std::string& data)
41         {
42                 irc::sepstream ss(data, ':');
43                 std::string tok;
44
45                 ss.GetToken(tok);
46                 this->iterations = ConvToInt(tok);
47
48                 ss.GetToken(tok);
49                 this->hash = Base64ToBin(tok);
50
51                 ss.GetToken(tok);
52                 this->salt = Base64ToBin(tok);
53
54                 this->length = this->hash.length();
55         }
56
57         std::string ToString()
58         {
59                 if (!IsValid())
60                         return "";
61                 return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
62         }
63
64         bool IsValid()
65         {
66                 if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
67                         return false;
68                 return true;
69         }
70 };
71
72 class PBKDF2Provider : public HashProvider
73 {
74  public:
75         HashProvider* provider;
76         unsigned int iterations;
77         unsigned int dkey_length;
78
79         std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
80         {
81                 size_t blocks = std::ceil((double)dkl / provider->out_size);
82
83                 std::string output;
84                 std::string tmphash;
85                 std::string salt_block = salt;
86                 for (size_t block = 1; block <= blocks; block++)
87                 {
88                         char salt_data[4];
89                         for (size_t i = 0; i < sizeof(salt_data); i++)
90                                 salt_data[i] = block >> (24 - i * 8) & 0x0F;
91
92                         salt_block.erase(salt.length());
93                         salt_block.append(salt_data, sizeof(salt_data));
94
95                         std::string blockdata = provider->hmac(pass, salt_block);
96                         std::string lasthash = blockdata;
97                         for (size_t iter = 1; iter < itr; iter++)
98                         {
99                                 tmphash = provider->hmac(pass, lasthash);
100                                 for (size_t i = 0; i < provider->out_size; i++)
101                                         blockdata[i] ^= tmphash[i];
102
103                                 lasthash.swap(tmphash);
104                         }
105                         output += blockdata;
106                 }
107
108                 output.erase(dkl);
109                 return output;
110         }
111
112         std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
113         {
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();
117         }
118
119         bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
120         {
121                 PBKDF2Hash hs(hash);
122                 if (!hs.IsValid())
123                         return false;
124
125                 std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
126                 return (cmp == hs.hash);
127         }
128
129         std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
130         {
131                 return raw;
132         }
133
134         PBKDF2Provider(Module* mod, HashProvider* hp)
135                 : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
136                 , provider(hp)
137         {
138                 DisableAutoRegister();
139         }
140 };
141
142 class ModulePBKDF2 : public Module
143 {
144         std::vector<PBKDF2Provider*> providers;
145
146         void GetConfig()
147         {
148                 // First set the common values
149                 ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
150                 unsigned int global_iterations = tag->getInt("iterations", 12288, 1);
151                 unsigned int global_dkey_length = tag->getInt("length", 32, 1, 1024);
152                 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
153                 {
154                         PBKDF2Provider* pi = *i;
155                         pi->iterations = global_iterations;
156                         pi->dkey_length = global_dkey_length;
157                 }
158
159                 // Then the specific values
160                 ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
161                 for (ConfigIter i = tags.first; i != tags.second; ++i)
162                 {
163                         tag = i->second;
164                         std::string hash_name = "hash/" + tag->getString("hash");
165                         for (std::vector<PBKDF2Provider*>::iterator j = providers.begin(); j != providers.end(); ++j)
166                         {
167                                 PBKDF2Provider* pi = *j;
168                                 if (pi->provider->name != hash_name)
169                                         continue;
170
171                                 pi->iterations = tag->getInt("iterations", global_iterations, 1);
172                                 pi->dkey_length = tag->getInt("length", global_dkey_length, 1, 1024);
173                         }
174                 }
175         }
176
177  public:
178         ~ModulePBKDF2()
179         {
180                 stdalgo::delete_all(providers);
181         }
182
183         void Prioritize() CXX11_OVERRIDE
184         {
185                 OnLoadModule(NULL);
186         }
187
188         void OnLoadModule(Module* mod) CXX11_OVERRIDE
189         {
190                 bool newProv = false;
191                 // As the module doesn't tell us what ServiceProviders it has, let's iterate all (yay ...) the ServiceProviders
192                 // Good thing people don't run loading and unloading those all the time
193                 for (std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.begin(); i != ServerInstance->Modules->DataProviders.end(); ++i)
194                 {
195                         ServiceProvider* provider = i->second;
196
197                         // Does the service belong to the new mod?
198                         // In the case this is our first run (mod == NULL, continue anyway)
199                         if (mod && provider->creator != mod)
200                                 continue;
201
202                         // Check if it's a hash provider
203                         if (provider->name.compare(0, 5, "hash/"))
204                                 continue;
205
206                         HashProvider* hp = static_cast<HashProvider*>(provider);
207
208                         if (hp->IsKDF())
209                                 continue;
210
211                         bool has_prov = false;
212                         for (std::vector<PBKDF2Provider*>::const_iterator j = providers.begin(); j != providers.end(); ++j)
213                         {
214                                 if ((*j)->provider == hp)
215                                 {
216                                         has_prov = true;
217                                         break;
218                                 }
219                         }
220                         if (has_prov)
221                                 continue;
222
223                         newProv = true;
224
225                         PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
226                         providers.push_back(prov);
227                         ServerInstance->Modules->AddService(*prov);
228                 }
229
230                 if (newProv)
231                         GetConfig();
232         }
233
234         void OnUnloadModule(Module* mod) CXX11_OVERRIDE
235         {
236                 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); )
237                 {
238                         PBKDF2Provider* item = *i;
239                         if (item->provider->creator != mod)
240                         {
241                                 ++i;
242                                 continue;
243                         }
244
245                         ServerInstance->Modules->DelService(*item);
246                         delete item;
247                         i = providers.erase(i);
248                 }
249         }
250
251         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
252         {
253                 GetConfig();
254         }
255
256         Version GetVersion() CXX11_OVERRIDE
257         {
258                 return Version("Implements PBKDF2 hashing", VF_VENDOR);
259         }
260 };
261
262 MODULE_INIT(ModulePBKDF2)