Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
13 KiB

  1. /*-----------------------------------------------------------------------------
  2. * Copyright (C) Microsoft Corporation, 1995 - 1996.
  3. * All rights reserved.
  4. *
  5. * This file is part of the Microsoft Private Communication Technology
  6. * reference implementation, version 1.0
  7. *
  8. * The Private Communication Technology reference implementation, version 1.0
  9. * ("PCTRef"), is being provided by Microsoft to encourage the development and
  10. * enhancement of an open standard for secure general-purpose business and
  11. * personal communications on open networks. Microsoft is distributing PCTRef
  12. * at no charge irrespective of whether you use PCTRef for non-commercial or
  13. * commercial use.
  14. *
  15. * Microsoft expressly disclaims any warranty for PCTRef and all derivatives of
  16. * it. PCTRef and any related documentation is provided "as is" without
  17. * warranty of any kind, either express or implied, including, without
  18. * limitation, the implied warranties or merchantability, fitness for a
  19. * particular purpose, or noninfringement. Microsoft shall have no obligation
  20. * to provide maintenance, support, upgrades or new releases to you or to anyone
  21. * receiving from you PCTRef or your modifications. The entire risk arising out
  22. * of use or performance of PCTRef remains with you.
  23. *
  24. * Please see the file LICENSE.txt,
  25. * or http://pct.microsoft.com/pct/pctlicen.txt
  26. * for more information on licensing.
  27. *
  28. * Please see http://pct.microsoft.com/pct/pct.htm for The Private
  29. * Communication Technology Specification version 1.0 ("PCT Specification")
  30. *
  31. * 1/23/96
  32. *----------------------------------------------------------------------------*/
  33. //
  34. // This file exports four functions: InitializeRNG, ShutdownRNG, GenRandom, and
  35. // GenerateRandomBits, which are used to generate random sequences of bytes.
  36. //
  37. #include <windows.h>
  38. #include <rng.h>
  39. #include <rc4.h>
  40. #include <sha.h>
  41. #define A_SHA_DIGEST_LEN 20
  42. #define RAND_CTXT_LEN 60
  43. #define RC4_REKEY_PARAM 500 // rekey every 500 bytes
  44. #define UNLEN MAX_PATH
  45. typedef struct _RandContext
  46. {
  47. DWORD dwBitsFilled;
  48. BYTE rgbBitBuffer[RAND_CTXT_LEN];
  49. } RandContext;
  50. #if 0
  51. CRITICAL_SECTION csRNG;
  52. #define LockRNG() EnterCriticalSection( &csRNG )
  53. #define UnlockRNG() LeaveCriticalSection( &csRNG )
  54. #else
  55. #define LockRNG()
  56. #define UnlockRNG()
  57. #endif
  58. unsigned char g_rgbStaticBits[A_SHA_DIGEST_LEN];
  59. static DWORD g_dwRC4BytesUsed = RC4_REKEY_PARAM; // initially force rekey
  60. static struct RC4_KEYSTRUCT g_rc4key;
  61. static BOOL RandomFillBuffer(BYTE *pbBuffer, DWORD *pdwLength);
  62. static void GatherRandomBits(RandContext *prandContext);
  63. static void AppendRand(RandContext* prandContext, void* pv, DWORD dwSize);
  64. /*****************************************************************************/
  65. VOID STInitializeRNG(VOID)
  66. {
  67. DWORD dwType;
  68. DWORD dwSize;
  69. LONG err;
  70. #if 0
  71. InitializeCriticalSection( &csRNG );
  72. LockRNG();
  73. // grab seed from persistent storage
  74. // SPQueryPersistentSeed(g_rgbStaticBits, A_SHA_DIGEST_LEN);
  75. #endif
  76. g_dwRC4BytesUsed = RC4_REKEY_PARAM;
  77. #if 0
  78. UnlockRNG();
  79. #endif
  80. return;
  81. }
  82. VOID ShutdownRNG(VOID)
  83. {
  84. #if 0
  85. DeleteCriticalSection( &csRNG );
  86. #endif
  87. // put seed into persistent storage
  88. // SPSetPersistentSeed(g_rgbStaticBits, A_SHA_DIGEST_LEN);
  89. return;
  90. }
  91. /*****************************************************************************/
  92. int STGenRandom(PVOID Reserved,
  93. UCHAR *pbBuffer,
  94. size_t dwLength)
  95. {
  96. STGenerateRandomBits(pbBuffer, dwLength);
  97. return TRUE;
  98. }
  99. /************************************************************************/
  100. /* GenerateRandomBits generates a specified number of random bytes and */
  101. /* places them into the specified buffer. */
  102. /************************************************************************/
  103. /* */
  104. /* Pseudocode logic flow: */
  105. /* */
  106. /* if (bits streamed > threshold) */
  107. /* { */
  108. /* Gather_Bits() */
  109. /* SHAMix_Bits(User, Gathered, Static -> Static) */
  110. /* RC4Key(Static -> newRC4Key) */
  111. /* SHABits(Static -> Static) // hash after RC4 key generation */
  112. /* } */
  113. /* else */
  114. /* { */
  115. /* SHAMix_Bits(User, Static -> Static) */
  116. /* } */
  117. /* */
  118. /* RC4(newRC4Key -> outbuf) */
  119. /* bits streamed += sizeof(outbuf) */
  120. /* */
  121. /************************************************************************/
  122. VOID STGenerateRandomBits(PUCHAR pbBuffer,
  123. ULONG dwLength)
  124. {
  125. DWORD dwBytesThisPass;
  126. DWORD dwFilledBytes = 0;
  127. // break request into chunks that we rekey between
  128. LockRNG();
  129. while(dwFilledBytes < dwLength)
  130. {
  131. dwBytesThisPass = dwLength - dwFilledBytes;
  132. RandomFillBuffer(pbBuffer + dwFilledBytes, &dwBytesThisPass);
  133. dwFilledBytes += dwBytesThisPass;
  134. }
  135. UnlockRNG();
  136. }
  137. /*****************************************************************************/
  138. static BOOL RandomFillBuffer(BYTE *pbBuffer, DWORD *pdwLength)
  139. {
  140. // Variables from loading and storing the registry...
  141. DWORD cbDataLen;
  142. cbDataLen = A_SHA_DIGEST_LEN;
  143. if(g_dwRC4BytesUsed >= RC4_REKEY_PARAM) {
  144. // if we need to rekey
  145. RandContext randContext;
  146. randContext.dwBitsFilled = 0;
  147. GatherRandomBits(&randContext);
  148. // Mix all bits
  149. {
  150. A_SHA_CTX SHACtx;
  151. A_SHAInit(&SHACtx);
  152. // SHA the static bits
  153. A_SHAUpdate(&SHACtx, g_rgbStaticBits, A_SHA_DIGEST_LEN);
  154. // SHA the gathered bits
  155. A_SHAUpdate(&SHACtx, randContext.rgbBitBuffer, randContext.dwBitsFilled);
  156. // SHA the user-supplied bits
  157. A_SHAUpdate(&SHACtx, pbBuffer, *pdwLength);
  158. // output back out to static bits
  159. A_SHAFinal(&SHACtx, g_rgbStaticBits);
  160. }
  161. // Create RC4 key
  162. g_dwRC4BytesUsed = 0;
  163. rc4_key(&g_rc4key, A_SHA_DIGEST_LEN, g_rgbStaticBits);
  164. // Mix RC4 key bits around
  165. {
  166. A_SHA_CTX SHACtx;
  167. A_SHAInit(&SHACtx);
  168. // SHA the static bits
  169. A_SHAUpdate(&SHACtx, g_rgbStaticBits, A_SHA_DIGEST_LEN);
  170. // output back out to static bits
  171. A_SHAFinal(&SHACtx, g_rgbStaticBits);
  172. }
  173. } else {
  174. // Use current RC4 key, but capture any user-supplied bits.
  175. // Mix input bits
  176. {
  177. A_SHA_CTX SHACtx;
  178. A_SHAInit(&SHACtx);
  179. // SHA the static bits
  180. A_SHAUpdate(&SHACtx, g_rgbStaticBits, A_SHA_DIGEST_LEN);
  181. // SHA the user-supplied bits
  182. A_SHAUpdate(&SHACtx, pbBuffer, *pdwLength);
  183. // output back out to static bits
  184. A_SHAFinal(&SHACtx, g_rgbStaticBits);
  185. }
  186. }
  187. // only use RC4_REKEY_PARAM bytes from each RC4 key
  188. {
  189. DWORD dwMaxPossibleBytes = RC4_REKEY_PARAM - g_dwRC4BytesUsed;
  190. if(*pdwLength > dwMaxPossibleBytes) {
  191. *pdwLength = dwMaxPossibleBytes;
  192. }
  193. }
  194. FillMemory(pbBuffer, *pdwLength, 0);
  195. rc4(&g_rc4key, *pdwLength, pbBuffer);
  196. g_dwRC4BytesUsed += *pdwLength;
  197. return TRUE;
  198. }
  199. /*****************************************************************************/
  200. static void GatherRandomBits(RandContext *prandContext)
  201. {
  202. DWORD dwTmp;
  203. WORD wTmp;
  204. BYTE bTmp;
  205. // ** indicates US DoD's specific recommendations for password generation
  206. // proc id
  207. dwTmp = GetCurrentProcessId();
  208. AppendRand(prandContext, &dwTmp, sizeof(dwTmp));
  209. // thread id
  210. dwTmp = GetCurrentThreadId();
  211. AppendRand(prandContext, &dwTmp, sizeof(dwTmp));
  212. // ** ticks since boot (system clock)
  213. dwTmp = GetTickCount();
  214. AppendRand(prandContext, &dwTmp, sizeof(dwTmp));
  215. // cursor position
  216. {
  217. POINT point;
  218. GetCursorPos(&point);
  219. bTmp = LOBYTE(point.x) ^ HIBYTE(point.x);
  220. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  221. bTmp = LOBYTE(point.y) ^ HIBYTE(point.y);
  222. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  223. }
  224. // ** system time, in ms, sec, min (date & time)
  225. {
  226. SYSTEMTIME sysTime;
  227. GetLocalTime(&sysTime);
  228. AppendRand(prandContext, &sysTime.wMilliseconds, sizeof(sysTime.wMilliseconds));
  229. bTmp = LOBYTE(sysTime.wSecond) ^ LOBYTE(sysTime.wMinute);
  230. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  231. }
  232. // ** hi-res performance counter (system counters)
  233. {
  234. LARGE_INTEGER liPerfCount;
  235. if(QueryPerformanceCounter(&liPerfCount)) {
  236. bTmp = LOBYTE(LOWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart));
  237. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  238. bTmp = HIBYTE(LOWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart));
  239. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  240. bTmp = LOBYTE(HIWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart));
  241. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  242. bTmp = HIBYTE(HIWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart));
  243. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  244. }
  245. }
  246. // memory status
  247. {
  248. MEMORYSTATUS mstMemStat;
  249. mstMemStat.dwLength = sizeof(MEMORYSTATUS); // must-do
  250. GlobalMemoryStatus(&mstMemStat);
  251. wTmp = HIWORD(mstMemStat.dwAvailPhys); // low words seem to be always zero
  252. AppendRand(prandContext, &wTmp, sizeof(WORD));
  253. wTmp = HIWORD(mstMemStat.dwAvailPageFile);
  254. AppendRand(prandContext, &wTmp, sizeof(WORD));
  255. bTmp = LOBYTE(HIWORD(mstMemStat.dwAvailVirtual));
  256. AppendRand(prandContext, &bTmp, sizeof(BYTE));
  257. }
  258. // free disk clusters
  259. {
  260. DWORD dwSectorsPerCluster, dwBytesPerSector, dwNumberOfFreeClusters, dwTotalNumberOfClusters;
  261. if(GetDiskFreeSpace(NULL, &dwSectorsPerCluster, &dwBytesPerSector, &dwNumberOfFreeClusters, &dwTotalNumberOfClusters)) {
  262. AppendRand(prandContext, &dwNumberOfFreeClusters, sizeof(dwNumberOfFreeClusters));
  263. AppendRand(prandContext, &dwTotalNumberOfClusters, sizeof(dwTotalNumberOfClusters));
  264. AppendRand(prandContext, &dwBytesPerSector, sizeof(dwBytesPerSector));
  265. }
  266. }
  267. // last messages' timestamp
  268. {
  269. LONG lTime;
  270. lTime = GetMessageTime();
  271. AppendRand(prandContext, &lTime, sizeof(lTime));
  272. }
  273. {
  274. static DWORD dwComputerNameSize;
  275. static DWORD dwUserNameSize;
  276. static char lpComputerName [MAX_COMPUTERNAME_LENGTH + 1];
  277. static char lpUserName [UNLEN + 1];
  278. //
  279. // note use of two temp DWORDs - that's to keep the static DWORDs
  280. // thread safe
  281. //
  282. // **SystemID
  283. if(dwComputerNameSize == 0) {
  284. DWORD dwTempComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
  285. if(GetComputerNameA(lpComputerName, &dwTempComputerNameSize))
  286. dwComputerNameSize = dwTempComputerNameSize;
  287. }
  288. if(dwComputerNameSize != 0) {
  289. // dwComputerNameSize = len not including null termination
  290. AppendRand(prandContext, lpComputerName, dwComputerNameSize);
  291. }
  292. // **UserID
  293. if(dwUserNameSize == 0) {
  294. DWORD dwTempUserNameSize = UNLEN + 1;
  295. if(GetUserNameA(lpUserName, &dwTempUserNameSize)) {
  296. // dwUserNameSize = len including null termination
  297. dwUserNameSize = dwTempUserNameSize - 1;
  298. }
  299. }
  300. if(dwUserNameSize != 0) {
  301. AppendRand(prandContext, lpUserName, dwUserNameSize);
  302. }
  303. }
  304. }
  305. /*****************************************************************************/
  306. static void AppendRand(RandContext* prandContext, void* pv, DWORD dwSize)
  307. {
  308. DWORD dwBitsLeft = (RAND_CTXT_LEN - prandContext->dwBitsFilled);
  309. if(dwBitsLeft > 0) {
  310. if(dwSize > dwBitsLeft) {
  311. dwSize = dwBitsLeft;
  312. }
  313. CopyMemory(prandContext->rgbBitBuffer + prandContext->dwBitsFilled, pv, dwSize);
  314. prandContext->dwBitsFilled += dwSize;
  315. }
  316. }