fortuna.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. fortuna.c
  5. Abstract:
  6. This module implements support for the Fortuna Pseudo-Random Number
  7. Generator.
  8. Author:
  9. Evan Green 13-Jan-2015
  10. Environment:
  11. Any
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include "cryptop.h"
  17. //
  18. // ---------------------------------------------------------------- Definitions
  19. //
  20. #define FORTUNA_POOL0_FILL 32
  21. #define FORTUNA_RESEED_SIZE (1024 * 1024)
  22. #define FORTUNA_RESEED_INTERVAL_MILLISECONDS 100
  23. //
  24. // ------------------------------------------------------ Data Type Definitions
  25. //
  26. //
  27. // ----------------------------------------------- Internal Function Prototypes
  28. //
  29. VOID
  30. CypFortunaSpreadInitialSeed (
  31. PFORTUNA_CONTEXT Context
  32. );
  33. VOID
  34. CypFortunaRekey (
  35. PFORTUNA_CONTEXT Context
  36. );
  37. BOOL
  38. CypFortunaCheckReseedTime (
  39. PFORTUNA_CONTEXT Context
  40. );
  41. VOID
  42. CypFortunaReseed (
  43. PFORTUNA_CONTEXT Context
  44. );
  45. VOID
  46. CypFortunaEncryptCounter (
  47. PFORTUNA_CONTEXT Context,
  48. UCHAR Buffer[FORTUNA_BLOCK_SIZE]
  49. );
  50. VOID
  51. CypFortunaIncrementCounter (
  52. PFORTUNA_CONTEXT Context
  53. );
  54. UINTN
  55. CypFortunaGetRandomPoolIndex (
  56. PFORTUNA_CONTEXT Context
  57. );
  58. //
  59. // -------------------------------------------------------------------- Globals
  60. //
  61. //
  62. // ------------------------------------------------------------------ Functions
  63. //
  64. CRYPTO_API
  65. VOID
  66. CyFortunaInitialize (
  67. PFORTUNA_CONTEXT Context,
  68. PCY_GET_TIME_COUNTER GetTimeCounterFunction,
  69. ULONGLONG TimeCounterFrequency
  70. )
  71. /*++
  72. Routine Description:
  73. This routine initializes a Fortuna PRNG context. It does not seed it with
  74. any values.
  75. Arguments:
  76. Context - Supplies a pointer to the context.
  77. GetTimeCounterFunction - Supplies an optional pointer to a function that
  78. can be used to retrieve a monotonically non-decreasing value
  79. representing the passage of time since some epoch.
  80. TimeCounterFrequency - Supplies the frequency of the time counter in
  81. Hertz.
  82. Return Value:
  83. None.
  84. --*/
  85. {
  86. UINTN Pool;
  87. RtlZeroMemory(Context, sizeof(FORTUNA_CONTEXT));
  88. for (Pool = 0; Pool < FORTUNA_POOL_COUNT; Pool += 1) {
  89. CySha256Initialize(&(Context->Pools[Pool]));
  90. }
  91. Context->GetTimeCounter = GetTimeCounterFunction;
  92. Context->TimeCounterFrequency = TimeCounterFrequency;
  93. return;
  94. }
  95. CRYPTO_API
  96. VOID
  97. CyFortunaGetRandomBytes (
  98. PFORTUNA_CONTEXT Context,
  99. PUCHAR Data,
  100. UINTN Size
  101. )
  102. /*++
  103. Routine Description:
  104. This routine returns random bytes from a Fortuna instance.
  105. Arguments:
  106. Context - Supplies a pointer to the context.
  107. Data - Supplies a pointer where the random bytes will be returned.
  108. Size - Supplies the number of bytes to return.
  109. Return Value:
  110. None.
  111. --*/
  112. {
  113. UINTN BlockNumber;
  114. UINTN CopySize;
  115. BlockNumber = 0;
  116. ASSERT(Context->Initialized != FortunaNotInitialized);
  117. //
  118. // Spread the seed around a bit if this is the first time.
  119. //
  120. if (Context->Initialized < FortunaInitialized) {
  121. CypFortunaSpreadInitialSeed(Context);
  122. Context->Initialized = FortunaInitialized;
  123. }
  124. //
  125. // Determine if a reseed should occur.
  126. //
  127. if ((Context->Pool0Bytes >= FORTUNA_POOL0_FILL) ||
  128. (Context->ReseedCount == 0)) {
  129. if (CypFortunaCheckReseedTime(Context) != FALSE) {
  130. CypFortunaReseed(Context);
  131. }
  132. }
  133. while (Size > 0) {
  134. CypFortunaEncryptCounter(Context, Context->Result);
  135. CopySize = Size;
  136. if (CopySize > FORTUNA_BLOCK_SIZE) {
  137. CopySize = FORTUNA_BLOCK_SIZE;
  138. }
  139. RtlCopyMemory(Data, Context->Result, CopySize);
  140. Data += CopySize;
  141. Size -= CopySize;
  142. //
  143. // Avoid giving out too many bytes from a single key.
  144. //
  145. BlockNumber += 1;
  146. if (BlockNumber > (FORTUNA_RESEED_SIZE / FORTUNA_BLOCK_SIZE)) {
  147. CypFortunaRekey(Context);
  148. BlockNumber = 0;
  149. }
  150. }
  151. //
  152. // Re-key for the next request.
  153. //
  154. CypFortunaRekey(Context);
  155. return;
  156. }
  157. CRYPTO_API
  158. VOID
  159. CyFortunaAddEntropy (
  160. PFORTUNA_CONTEXT Context,
  161. PVOID Data,
  162. UINTN Size
  163. )
  164. /*++
  165. Routine Description:
  166. This routine adds random data into the mix.
  167. Arguments:
  168. Context - Supplies a pointer to the context.
  169. Data - Supplies a pointer to the data to add.
  170. Size - Supplies the number of bytes of randomness in the data buffer.
  171. Return Value:
  172. None.
  173. --*/
  174. {
  175. UCHAR Hash[SHA256_HASH_SIZE];
  176. SHA256_CONTEXT HashContext;
  177. UINTN PoolIndex;
  178. //
  179. // Hash the data handed in.
  180. //
  181. CySha256Initialize(&HashContext);
  182. CySha256AddContent(&HashContext, Data, Size);
  183. CySha256GetHash(&HashContext, Hash);
  184. //
  185. // Make sure pool zero is initialized, otherwise update randomly.
  186. //
  187. if (Context->ReseedCount == 0) {
  188. PoolIndex = 0;
  189. if (Context->Initialized == FortunaNotInitialized) {
  190. Context->Initialized = FortunaInitializationSeeded;
  191. }
  192. } else {
  193. PoolIndex = CypFortunaGetRandomPoolIndex(Context);
  194. }
  195. CySha256AddContent(&(Context->Pools[PoolIndex]), Hash, SHA256_HASH_SIZE);
  196. if (PoolIndex == 0) {
  197. Context->Pool0Bytes += SHA256_HASH_SIZE;
  198. }
  199. return;
  200. }
  201. //
  202. // --------------------------------------------------------- Internal Functions
  203. //
  204. VOID
  205. CypFortunaSpreadInitialSeed (
  206. PFORTUNA_CONTEXT Context
  207. )
  208. /*++
  209. Routine Description:
  210. This routine spreads the entropy gained so far around the pools.
  211. Arguments:
  212. Context - Supplies a pointer to the context.
  213. Return Value:
  214. None.
  215. --*/
  216. {
  217. UCHAR Buffer[FORTUNA_HASH_KEY_SIZE];
  218. UINTN PoolIndex;
  219. //
  220. // Use the next block as the initial counter value.
  221. //
  222. CypFortunaEncryptCounter(Context, Context->Counter);
  223. //
  224. // Shuffle all pools except pool zero.
  225. //
  226. for (PoolIndex = 0; PoolIndex < FORTUNA_POOL_COUNT; PoolIndex += 1) {
  227. CypFortunaEncryptCounter(Context, Buffer);
  228. CypFortunaEncryptCounter(Context, Buffer + FORTUNA_BLOCK_SIZE);
  229. CySha256AddContent(&(Context->Pools[PoolIndex]),
  230. Buffer,
  231. FORTUNA_HASH_KEY_SIZE);
  232. }
  233. RtlZeroMemory(Buffer, sizeof(Buffer));
  234. //
  235. // Hide the key.
  236. //
  237. CypFortunaRekey(Context);
  238. return;
  239. }
  240. VOID
  241. CypFortunaRekey (
  242. PFORTUNA_CONTEXT Context
  243. )
  244. /*++
  245. Routine Description:
  246. This routine selects a different cipher key for use in future block
  247. generation.
  248. Arguments:
  249. Context - Supplies a pointer to the context.
  250. Return Value:
  251. None.
  252. --*/
  253. {
  254. //
  255. // Use the two next blocks as the new key.
  256. //
  257. CypFortunaEncryptCounter(Context, Context->Key);
  258. CypFortunaEncryptCounter(Context, Context->Key + FORTUNA_BLOCK_SIZE);
  259. CyAesInitialize(&(Context->CipherContext),
  260. AesModeCbc256,
  261. Context->Key,
  262. NULL);
  263. return;
  264. }
  265. BOOL
  266. CypFortunaCheckReseedTime (
  267. PFORTUNA_CONTEXT Context
  268. )
  269. /*++
  270. Routine Description:
  271. This routine determines whether it is time for the context to be reseeded
  272. or not.
  273. Arguments:
  274. Context - Supplies a pointer to the context.
  275. Return Value:
  276. TRUE if enough time has passed such that the context can be reseeded.
  277. FALSE if it hasn't been long enough since the last reseed.
  278. --*/
  279. {
  280. ULONGLONG CurrentTime;
  281. ULONGLONG DeltaTicks;
  282. ULONGLONG Milliseconds;
  283. if ((Context->GetTimeCounter == NULL) ||
  284. (Context->TimeCounterFrequency == 0)) {
  285. return TRUE;
  286. }
  287. CurrentTime = Context->GetTimeCounter();
  288. DeltaTicks = CurrentTime - Context->LastReseedTime;
  289. //
  290. // Compute the number of milliseconds since the last update.
  291. //
  292. Milliseconds = (DeltaTicks * 1000ULL) / Context->TimeCounterFrequency;
  293. if (Milliseconds >= FORTUNA_RESEED_INTERVAL_MILLISECONDS) {
  294. Context->LastReseedTime = CurrentTime;
  295. return TRUE;
  296. }
  297. return FALSE;
  298. }
  299. VOID
  300. CypFortunaReseed (
  301. PFORTUNA_CONTEXT Context
  302. )
  303. /*++
  304. Routine Description:
  305. This routine selects a completely new cipher key using the entropy pools.
  306. Arguments:
  307. Context - Supplies a pointer to the context.
  308. Return Value:
  309. None.
  310. --*/
  311. {
  312. UCHAR Buffer[FORTUNA_HASH_KEY_SIZE];
  313. SHA256_CONTEXT KeyHashContext;
  314. UINTN PoolIndex;
  315. UINTN ReseedCount;
  316. //
  317. // Mark the pool as empty.
  318. //
  319. Context->Pool0Bytes = 0;
  320. Context->ReseedCount += 1;
  321. ReseedCount = Context->ReseedCount;
  322. //
  323. // The 0th pool is used every 2nd time, the 1st pool is used every 4th
  324. // time, the 2nd pool is used every 8th time, etc. Pool i is used every
  325. // 2^ith time.
  326. //
  327. CySha256Initialize(&KeyHashContext);
  328. for (PoolIndex = 0; PoolIndex < FORTUNA_POOL_COUNT; PoolIndex += 1) {
  329. CySha256GetHash(&(Context->Pools[PoolIndex]), Buffer);
  330. CySha256AddContent(&KeyHashContext, Buffer, FORTUNA_HASH_KEY_SIZE);
  331. if (((ReseedCount & 0x1) != 0) || (ReseedCount == 0)) {
  332. break;
  333. }
  334. ReseedCount >>= 1;
  335. }
  336. //
  337. // Add the old key into the mix too.
  338. //
  339. CySha256AddContent(&KeyHashContext, Context->Key, FORTUNA_HASH_KEY_SIZE);
  340. //
  341. // Get the new key.
  342. //
  343. CySha256GetHash(&KeyHashContext, Context->Key);
  344. //
  345. // Use the new key in future cipher blocks.
  346. //
  347. CyAesInitialize(&(Context->CipherContext),
  348. AesModeCbc256,
  349. Context->Key,
  350. NULL);
  351. //
  352. // Avoid leaking state onto the stack.
  353. //
  354. RtlZeroMemory(Buffer, sizeof(Buffer));
  355. RtlZeroMemory(&KeyHashContext, sizeof(SHA256_CONTEXT));
  356. return;
  357. }
  358. VOID
  359. CypFortunaEncryptCounter (
  360. PFORTUNA_CONTEXT Context,
  361. UCHAR Buffer[FORTUNA_BLOCK_SIZE]
  362. )
  363. /*++
  364. Routine Description:
  365. This routine simply encrypts a new counter value.
  366. Arguments:
  367. Context - Supplies a pointer to the context.
  368. Buffer - Supplies a pointer where the new block will be returned.
  369. Return Value:
  370. None.
  371. --*/
  372. {
  373. CyAesCbcEncrypt(&(Context->CipherContext),
  374. Context->Counter,
  375. Buffer,
  376. FORTUNA_BLOCK_SIZE);
  377. CypFortunaIncrementCounter(Context);
  378. return;
  379. }
  380. VOID
  381. CypFortunaIncrementCounter (
  382. PFORTUNA_CONTEXT Context
  383. )
  384. /*++
  385. Routine Description:
  386. This routine simply increments the current counter value.
  387. Arguments:
  388. Context - Supplies a pointer to the context.
  389. Return Value:
  390. None.
  391. --*/
  392. {
  393. PUINTN Value;
  394. Value = (PUINTN)(Context->Counter);
  395. Value[0] += 1;
  396. if (Value[0] != 0) {
  397. return;
  398. }
  399. Value[1] += 1;
  400. if (Value[1] != 0) {
  401. return;
  402. }
  403. Value[2] += 1;
  404. if (Value[2] != 0) {
  405. return;
  406. }
  407. Value[3] += 1;
  408. return;
  409. }
  410. UINTN
  411. CypFortunaGetRandomPoolIndex (
  412. PFORTUNA_CONTEXT Context
  413. )
  414. /*++
  415. Routine Description:
  416. This routine returns a pool index to feed entropy into.
  417. Arguments:
  418. Context - Supplies a pointer to the context.
  419. Return Value:
  420. Returns a pool index.
  421. --*/
  422. {
  423. UINTN Index;
  424. Index = Context->Key[Context->Position] % FORTUNA_POOL_COUNT;
  425. Context->Position += 1;
  426. if (Context->Position >= FORTUNA_HASH_KEY_SIZE) {
  427. Context->Position = 0;
  428. }
  429. return Index;
  430. }