SLikeNet 0.1.3

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TwoWayAuthentication.cpp
Go to the documentation of this file.
1 /*
2 * Original work: Copyright (c) 2014, Oculus VR, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * RakNet License.txt file in the licenses directory of this source tree. An additional grant
7 * of patent rights can be found in the RakNet Patents.txt file in the same directory.
8 *
9 *
10 * Modified work: Copyright (c) 2016-2017, SLikeSoft UG (haftungsbeschränkt)
11 *
12 * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
13 * license found in the license.txt file in the root directory of this source tree.
14 */
15
16 #include "slikenet/NativeFeatureIncludes.h"
17 #if _RAKNET_SUPPORT_TwoWayAuthentication==1
18
19 #include "slikenet/TwoWayAuthentication.h"
20 #include "slikenet/Rand.h"
21 #include "slikenet/GetTime.h"
22 #include "slikenet/MessageIdentifiers.h"
23 #include "slikenet/BitStream.h"
24 #include "slikenet/peerinterface.h"
25
26 #if LIBCAT_SECURITY==1
27 static const int HASH_BITS = 256;
28 static const int HASH_BYTES = HASH_BITS / 8;
29 static const int STRENGTHENING_FACTOR = 256;
30
31 // If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning
32 #if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL)
33 # define CAT_BUILD_DLL
34 #else
35 # define CAT_NEUTER_EXPORT
36 #endif
37 #include <cat/crypt/hash/Skein.hpp>
38 #endif
39
40 using namespace SLNet;
41
42 enum NegotiationIdentifiers
43 {
44 ID_NONCE_REQUEST,
45 ID_NONCE_REPLY,
46 ID_HASHED_NONCE_AND_PASSWORD,
47 };
48
49 TwoWayAuthentication::NonceGenerator::NonceGenerator() {nextRequestId=0;}
50 TwoWayAuthentication::NonceGenerator::~NonceGenerator()
51 {
52 Clear();
53 }
54 void TwoWayAuthentication::NonceGenerator::GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, SLNet::AddressOrGUID remoteSystem)
55 {
56 TwoWayAuthentication::NonceAndRemoteSystemRequest *narsr = SLNet::OP_NEW<TwoWayAuthentication::NonceAndRemoteSystemRequest>(_FILE_AND_LINE_);
57 narsr->remoteSystem=remoteSystem;
58 GenerateNonce(narsr->nonce);
59 narsr->requestId=nextRequestId++;
60 *requestId=narsr->requestId;
61 memcpy(nonce,narsr->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
62 narsr->whenGenerated= SLNet::GetTime();
63 generatedNonces.Push(narsr,_FILE_AND_LINE_);
64 }
65 void TwoWayAuthentication::NonceGenerator::GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH])
66 {
67 fillBufferMT(nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
68 }
69 bool TwoWayAuthentication::NonceGenerator::GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, SLNet::AddressOrGUID remoteSystem, bool popIfFound)
70 {
71 unsigned int i;
72 for (i=0; i < generatedNonces.Size(); i++)
73 {
74 if (generatedNonces[i]->requestId==requestId)
75 {
76 if (remoteSystem==generatedNonces[i]->remoteSystem)
77 {
78 memcpy(nonce,generatedNonces[i]->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
79 if (popIfFound)
80 {
81 SLNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_);
82 generatedNonces.RemoveAtIndex(i);
83 }
84 return true;
85 }
86 else
87 {
88 return false;
89 }
90 }
91 }
92 return false;
93 }
94 void TwoWayAuthentication::NonceGenerator::Clear(void)
95 {
96 unsigned int i;
97 for (i=0; i < generatedNonces.Size(); i++)
98 SLNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_);
99 generatedNonces.Clear(true,_FILE_AND_LINE_);
100 }
101 void TwoWayAuthentication::NonceGenerator::ClearByAddress(SLNet::AddressOrGUID remoteSystem)
102 {
103 unsigned int i=0;
104 while (i < generatedNonces.Size())
105 {
106 if (generatedNonces[i]->remoteSystem==remoteSystem)
107 {
108 SLNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_);
109 generatedNonces.RemoveAtIndex(i);
110 }
111 else
112 {
113 i++;
114 }
115 }
116 }
117 void TwoWayAuthentication::NonceGenerator::Update(SLNet::Time curTime)
118 {
119 if (generatedNonces.Size()>0 && GreaterThan(curTime-5000, generatedNonces[0]->whenGenerated))
120 {
121 SLNet::OP_DELETE(generatedNonces[0], _FILE_AND_LINE_);
122 generatedNonces.RemoveAtIndex(0);
123 }
124 }
125 TwoWayAuthentication::TwoWayAuthentication()
126 {
127 whenLastTimeoutCheck= SLNet::GetTime();
128 seedMT(SLNet::GetTimeMS());
129 }
130 TwoWayAuthentication::~TwoWayAuthentication()
131 {
132 Clear();
133 }
134 bool TwoWayAuthentication::AddPassword(SLNet::RakString identifier, SLNet::RakString password)
135 {
136 if (password.IsEmpty())
137 return false;
138
139 if (identifier.IsEmpty())
140 return false;
141
142 if (password==identifier)
143 return false; // Insecure
144
145 if (passwords.GetIndexOf(identifier.C_String()).IsInvalid()==false)
146 return false; // This identifier already in use
147
148 passwords.Push(identifier, password,_FILE_AND_LINE_);
149 return true;
150 }
151 bool TwoWayAuthentication::Challenge(SLNet::RakString identifier, AddressOrGUID remoteSystem)
152 {
153 DataStructures::HashIndex skhi = passwords.GetIndexOf(identifier.C_String());
154 if (skhi.IsInvalid())
155 return false;
156
157 SLNet::BitStream bsOut;
158 bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION);
159 bsOut.Write((MessageID)ID_NONCE_REQUEST);
160 SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,remoteSystem,false);
161
162 PendingChallenge pc;
163 pc.identifier=identifier;
164 pc.remoteSystem=remoteSystem;
165 pc.time= SLNet::GetTime();
166 pc.sentHash=false;
167 outgoingChallenges.Push(pc,_FILE_AND_LINE_);
168
169 return true;
170 }
171 void TwoWayAuthentication::Update(void)
172 {
173 SLNet::Time curTime = SLNet::GetTime();
174 nonceGenerator.Update(curTime);
175 if (GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, whenLastTimeoutCheck))
176 {
177 while (outgoingChallenges.Size() && GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, outgoingChallenges.Peek().time))
178 {
179 PendingChallenge pc = outgoingChallenges.Pop();
180
181 // Tell the user about the timeout
182 PushToUser(ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, pc.identifier, pc.remoteSystem);
183 }
184
185 whenLastTimeoutCheck=curTime+CHALLENGE_MINIMUM_TIMEOUT;
186 }
187 }
188 PluginReceiveResult TwoWayAuthentication::OnReceive(Packet *packet)
189 {
190 switch (packet->data[0])
191 {
192 case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION:
193 {
194 if (packet->length>=sizeof(MessageID)*2)
195 {
196 switch (packet->data[sizeof(MessageID)])
197 {
198 case ID_NONCE_REQUEST:
199 {
200 OnNonceRequest(packet);
201 }
202 break;
203 case ID_NONCE_REPLY:
204 {
205 OnNonceReply(packet);
206 }
207 break;
208 case ID_HASHED_NONCE_AND_PASSWORD:
209 {
210 return OnHashedNonceAndPassword(packet);
211 }
212 break;
213 }
214 }
215 return RR_STOP_PROCESSING_AND_DEALLOCATE;
216 }
217 case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE:
218 case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS:
219 {
220 if (packet->wasGeneratedLocally==false)
221 {
222 OnPasswordResult(packet);
223 return RR_STOP_PROCESSING_AND_DEALLOCATE;
224 }
225 else
226 break;
227 }
228 break;
229 // These should only be generated locally
230 case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS:
231 case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE:
232 case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT:
233 if (packet->wasGeneratedLocally==false)
234 return RR_STOP_PROCESSING_AND_DEALLOCATE;
235 break;
236 }
237
238 return RR_CONTINUE_PROCESSING;
239 }
240 void TwoWayAuthentication::OnRakPeerShutdown(void)
241 {
242 Clear();
243 }
244 void TwoWayAuthentication::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
245 {
246 (void) lostConnectionReason;
247
248 // Remove from pending challenges
249 unsigned int i=0;
250 while (i < outgoingChallenges.Size())
251 {
252 if ((rakNetGUID!=UNASSIGNED_RAKNET_GUID && outgoingChallenges[i].remoteSystem.rakNetGuid==rakNetGUID) ||
253 (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && outgoingChallenges[i].remoteSystem.systemAddress==systemAddress))
254 {
255 outgoingChallenges.RemoveAtIndex(i);
256 }
257 else
258 {
259 i++;
260 }
261 }
262
263 if (rakNetGUID!=UNASSIGNED_RAKNET_GUID)
264 nonceGenerator.ClearByAddress(rakNetGUID);
265 else
266 nonceGenerator.ClearByAddress(systemAddress);
267 }
268 void TwoWayAuthentication::Clear(void)
269 {
270 outgoingChallenges.Clear(_FILE_AND_LINE_);
271 passwords.Clear(_FILE_AND_LINE_);
272 nonceGenerator.Clear();
273 }
274 void TwoWayAuthentication::PushToUser(MessageID messageId, SLNet::RakString password, SLNet::AddressOrGUID remoteSystem)
275 {
276 SLNet::BitStream output;
277 output.Write(messageId);
278 if (password.IsEmpty()==false)
279 output.Write(password);
280 Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed());
281 p->systemAddress=remoteSystem.systemAddress;
282 p->systemAddress.systemIndex=(SystemIndex)-1;
283 p->guid=remoteSystem.rakNetGuid;
284 p->wasGeneratedLocally=true;
285 memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed());
286 rakPeerInterface->PushBackPacket(p, true);
287 }
288 void TwoWayAuthentication::OnNonceRequest(Packet *packet)
289 {
290 SLNet::BitStream bsIn(packet->data, packet->length, false);
291 bsIn.IgnoreBytes(sizeof(MessageID)*2);
292
293 char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
294 unsigned short requestId;
295 nonceGenerator.GetNonce(nonce,&requestId,packet);
296
297 SLNet::BitStream bsOut;
298 bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION);
299 bsOut.Write((MessageID)ID_NONCE_REPLY);
300 bsOut.Write(requestId);
301 bsOut.WriteAlignedBytes((const unsigned char*) nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
302 SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);
303 }
304 void TwoWayAuthentication::OnNonceReply(Packet *packet)
305 {
306 SLNet::BitStream bsIn(packet->data, packet->length, false);
307 bsIn.IgnoreBytes(sizeof(MessageID)*2);
308
309 char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
310 unsigned short requestId;
311 bsIn.Read(requestId);
312 bsIn.ReadAlignedBytes((unsigned char *) thierNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
313
314 // Lookup one of the negotiations for this guid/system address
315 AddressOrGUID aog(packet);
316 unsigned int i;
317 for (i=0; i < outgoingChallenges.Size(); i++)
318 {
319 if (outgoingChallenges[i].remoteSystem==aog && outgoingChallenges[i].sentHash==false)
320 {
321 outgoingChallenges[i].sentHash=true;
322
323 // Get the password for this identifier
324 DataStructures::HashIndex skhi = passwords.GetIndexOf(outgoingChallenges[i].identifier.C_String());
325 if (skhi.IsInvalid()==false)
326 {
327 SLNet::RakString password = passwords.ItemAtIndex(skhi);
328
329 // Hash their nonce with password and reply
330 char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
331 Hash(thierNonce, password, hashedNonceAndPw);
332
333 // Send
334 SLNet::BitStream bsOut;
335 bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION);
336 bsOut.Write((MessageID)ID_HASHED_NONCE_AND_PASSWORD);
337 bsOut.Write(requestId);
338 bsOut.Write(outgoingChallenges[i].identifier); // Identifier helps the other system lookup the password quickly.
339 bsOut.WriteAlignedBytes((const unsigned char*) hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
340 SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);
341 }
342
343 return;
344 }
345 }
346 }
347 PluginReceiveResult TwoWayAuthentication::OnHashedNonceAndPassword(Packet *packet)
348 {
349 SLNet::BitStream bsIn(packet->data, packet->length, false);
350 bsIn.IgnoreBytes(sizeof(MessageID)*2);
351
352 char remoteHashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
353 unsigned short requestId;
354 bsIn.Read(requestId);
355 SLNet::RakString passwordIdentifier;
356 bsIn.Read(passwordIdentifier);
357 bsIn.ReadAlignedBytes((unsigned char *) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
358
359 // Look up used nonce from requestId
360 char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
361 if (nonceGenerator.GetNonceById(usedNonce, requestId, packet, true)==false)
362 return RR_STOP_PROCESSING_AND_DEALLOCATE;
363
364 DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String());
365 if (skhi.IsInvalid()==false)
366 {
367 char hashedThisNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
368 Hash(usedNonce, passwords.ItemAtIndex(skhi), hashedThisNonceAndPw);
369 if (memcmp(hashedThisNonceAndPw, remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0)
370 {
371 // Pass
372 SLNet::BitStream bsOut;
373 bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS);
374 bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
375 bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
376 bsOut.Write(passwordIdentifier);
377 SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);
378
379 // Incoming success, modify packet header to tell user
380 PushToUser(ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, passwordIdentifier, packet);
381
382 return RR_STOP_PROCESSING_AND_DEALLOCATE;
383 }
384 }
385
386 // Incoming failure, modify arrived packet header to tell user
387 packet->data[0]=(MessageID) ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE;
388
389 SLNet::BitStream bsOut;
390 bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE);
391 bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
392 bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
393 bsOut.Write(passwordIdentifier);
394 SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);
395
396 return RR_CONTINUE_PROCESSING;
397 }
398 void TwoWayAuthentication::OnPasswordResult(Packet *packet)
399 {
400 SLNet::BitStream bsIn(packet->data, packet->length, false);
401 bsIn.IgnoreBytes(sizeof(MessageID)*1);
402 char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
403 bsIn.ReadAlignedBytes((unsigned char *)usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
404 char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
405 bsIn.ReadAlignedBytes((unsigned char *)hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
406 SLNet::RakString passwordIdentifier;
407 bsIn.Read(passwordIdentifier);
408
409 DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String());
410 if (skhi.IsInvalid()==false)
411 {
412 SLNet::RakString password = passwords.ItemAtIndex(skhi);
413 char testHash[HASHED_NONCE_AND_PW_LENGTH];
414 Hash(usedNonce, password, testHash);
415 if (memcmp(testHash,hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0)
416 {
417 // Lookup the outgoing challenge and remove it from the list
418 unsigned int i;
419 AddressOrGUID aog(packet);
420 for (i=0; i < outgoingChallenges.Size(); i++)
421 {
422 if (outgoingChallenges[i].identifier==passwordIdentifier &&
423 outgoingChallenges[i].remoteSystem==aog &&
424 outgoingChallenges[i].sentHash==true)
425 {
426 outgoingChallenges.RemoveAtIndex(i);
427
428 PushToUser(packet->data[0], passwordIdentifier, packet);
429 return;
430 }
431 }
432 }
433 }
434 }
435 void TwoWayAuthentication::Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], SLNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH])
436 {
437 #if LIBCAT_SECURITY==1
438 cat::Skein hash;
439 if (!hash.BeginKey(HASH_BITS)) return;
440 hash.Crunch(thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
441 hash.Crunch(password.C_String(), (int) password.GetLength());
442 hash.End();
443 hash.Generate(out, HASH_BYTES, STRENGTHENING_FACTOR);
444 #else
445 CSHA1 sha1;
446 sha1.Update((unsigned char *) thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
447 sha1.Update((unsigned char *) password.C_String(), (unsigned int) password.GetLength());
448 sha1.Final();
449 sha1.GetHash((unsigned char *) out);
450 #endif
451 }
452
453 #endif