TwoWayAuthentication.cpp 14 KB

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