1
0

Random.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. #include "rust/cjdns_sys/Rffi.h"
  16. #include "crypto/random/Random.h"
  17. #include "crypto/random/seed/RandomSeed.h"
  18. #include "crypto/random/seed/SystemRandomSeed.h"
  19. #include "memory/Allocator.h"
  20. #include "util/Bits.h"
  21. #include "util/Base32.h"
  22. #include "util/Identity.h"
  23. #include "util/Endian.h"
  24. #include "util/Hex.h"
  25. #include "util/Defined.h"
  26. #include "util/log/Log.h"
  27. #include <sodium/crypto_stream_salsa20.h>
  28. /**
  29. * cjdns random generator:
  30. * It is with great apprehension that I have decided to go forward with this random generator.
  31. * Sadly there doesn't exist any plain-and-simple random generation library for C without
  32. * bundling libevent, openssl or some other megalyth.
  33. *
  34. * Additionally most random generators use a feedback loop which is difficult to validate as
  35. * it has a period which is not immedietly obvious by looking at it. Additionally, this
  36. * feedback loop design leads to issues like:
  37. * http://www.openssl.org/news/secadv_prng.txt
  38. *
  39. * How this random generator works:
  40. * 1. All available random sources such as dev/urandom and sysctl(RANDOM_UUID) are combined
  41. * with a rolling SHA-512 hash, the result is placed in the Random_SeedGen union.
  42. *
  43. * 2. Random_SeedGen is SHA-256 hashed into Random.tempSeed
  44. *
  45. * 3. Random numbers are generated by running salsa20 with Random.tempSeed as the key, and
  46. * Random.nonce 64 bit counter which is incremented each run, never reset, and assumed
  47. * never to wrap.
  48. *
  49. * Adding entropy to the generator is as follows:
  50. * Random_addRandom() adds a sample of randomness by rotating and XORing it into
  51. * Random_SeedGen.collectedEntropy.
  52. * Every 256 calls to Random_addRandom(), Random_SeedGen is again hashed into Random.tempSeed.
  53. * Note that Random.nonce is *not* reset ever during the operation of the generator because
  54. * otherwise, 512 successive calls to Random_addRandom() with the same input would cause the
  55. * random generator to repeat.
  56. *
  57. *
  58. * State-compromize extension:
  59. * It is acknoliged that a compromize of the generator's internal state will result in the
  60. * attacker knowing every output which has been and will be generated or with the current
  61. * tempSeed. After a further 256 calls to Random_addRandom(), the generator should recover.
  62. *
  63. * While using a feedback loop with a one-way hash function to frustrate backtracking seems
  64. * enticing, it stands to reason that the only way a hash function can be one-way is by
  65. * destroying entropy, destruction of entropy in a feedback system could lead to an oscillation
  66. * effect when it becomes entropy starved. Though this issue does not seem to have been
  67. * exploited in other prngs, proving that it cannot be exploited is beyond my abilities and the
  68. * devil you know is better than the devil you don't.
  69. *
  70. *
  71. * Iterative Guessing:
  72. * This generator only introduces the entropy given by Random_addRandom() once every 256 calls.
  73. * Assuming each call introduces at least 1 bit of good entropy, iterative guessing requires
  74. * guessing a 256 bit value for each iteration.
  75. *
  76. *
  77. * Input based Attacks:
  78. * The generator is as conservitive as possible about the entropy provided in calls to
  79. * Random_addRandom(), valuing each at 1 bit of entropy. Since the number is rotated and XORd
  80. * into collectedEntropy, some calls with 0 bits of entropy can be smoothed over by other calls
  81. * with > 1 bit of entropy. If Random_addRandom() is called arbitrarily many times with 0 bits
  82. * of entropy, since the inputs are XORd into collectedEntropy the entropy level of
  83. * collectedEntropy will remain unchanged.
  84. *
  85. * Even if the attacker is able to gather information from the generator's output and craft
  86. * inputs to Random_addRandom() which *decrease* the entropy in collectedEntropy, this will not
  87. * decrease the performance of the generator itself because the 256 bit Random_SeedGen.seed
  88. * is seeded with the primary seed meterial (eg dev/urandom) and never altered for duration of
  89. * the generator's operation.
  90. */
  91. /** How many bytes to buffer so requests for a small amount of random do not invoke salsa20. */
  92. #define BUFFSIZE 128
  93. /** The key material which is used to generate the temporary seed. */
  94. union Random_SeedGen
  95. {
  96. struct {
  97. /**
  98. * Read directly from the seed supplier (dev/urandom etc.),
  99. * same for the whole run of the generator.
  100. */
  101. uint64_t seed[4];
  102. /**
  103. * Initialized by the seed supplier
  104. * then XORd with the input given to Random_addRandom().
  105. */
  106. uint32_t collectedEntropy[8];
  107. } elements;
  108. /** Used to generate tempSeed. */
  109. uint64_t buff[8];
  110. };
  111. struct Random
  112. {
  113. /** The random seed which is used to generate random numbers. */
  114. uint64_t tempSeed[4];
  115. /** Incremented every call to salsa20, never reset. */
  116. uint64_t nonce;
  117. /** buffer of random generated in the last rand cycle. */
  118. uint8_t buff[BUFFSIZE];
  119. /** the next number to read out of buff. */
  120. int nextByte;
  121. /** A counter which Random_addRandom() uses to rotate the random input. */
  122. int addRandomCounter;
  123. /** The seed generator for generating new temporary random seeds. */
  124. union Random_SeedGen* seedGen;
  125. /** The collector for getting the original permanent random seed from the operating system. */
  126. RandomSeed_t* seed;
  127. struct Allocator* alloc;
  128. struct Log* log;
  129. Identity
  130. };
  131. /**
  132. * Add a random number to the entropy pool.
  133. * 1 bit of entropy is extracted from each call to addRandom(), every 256 calls
  134. * this function will generate a new temporary seed using the permanent seed and
  135. * the collected entropy.
  136. *
  137. * Worst case scenario, Random_addRandom() is completely broken, the original
  138. * seed is still used and the nonce is never reset so the only loss is forward secrecy.
  139. */
  140. void Random_addRandom(struct Random* rand, uint32_t randomNumber)
  141. {
  142. Identity_check(rand);
  143. #define rotl(a,b) (((a) << (b)) | ((a) >> (31 - (b))))
  144. rand->seedGen->elements.collectedEntropy[rand->addRandomCounter % 8] ^=
  145. rotl(randomNumber, rand->addRandomCounter / 8);
  146. if (++rand->addRandomCounter >= 256) {
  147. Rffi_crypto_hash_sha256((uint8_t*)rand->tempSeed,
  148. (uint8_t*)rand->seedGen->buff,
  149. sizeof(union Random_SeedGen));
  150. rand->addRandomCounter = 0;
  151. }
  152. }
  153. static void stir(struct Random* rand)
  154. {
  155. uint64_t nonce = Endian_hostToLittleEndian64(rand->nonce);
  156. crypto_stream_salsa20_xor((uint8_t*)rand->buff,
  157. (uint8_t*)rand->buff,
  158. BUFFSIZE,
  159. (uint8_t*)&nonce,
  160. (uint8_t*)rand->tempSeed);
  161. rand->nonce++;
  162. rand->nextByte = 0;
  163. }
  164. static uintptr_t randomCopy(struct Random* rand, uint8_t* location, uint64_t count)
  165. {
  166. uintptr_t num = (uintptr_t) count;
  167. if (num > (uintptr_t)(BUFFSIZE - rand->nextByte)) {
  168. num = (BUFFSIZE - rand->nextByte);
  169. }
  170. Bits_memcpy(location, &rand->buff[rand->nextByte], num);
  171. rand->nextByte += num;
  172. return num;
  173. }
  174. void Random_bytes(struct Random* rand, uint8_t* location, uint64_t count)
  175. {
  176. Identity_check(rand);
  177. if (count > BUFFSIZE) {
  178. // big request, don't buffer it.
  179. crypto_stream_salsa20_xor((uint8_t*)location,
  180. (uint8_t*)location,
  181. count,
  182. (uint8_t*)&rand->nonce,
  183. (uint8_t*)rand->tempSeed);
  184. rand->nonce++;
  185. if (Defined(Log_KEYS)) {
  186. struct Allocator* alloc = Allocator_child(rand->alloc);
  187. char* buf = Hex_print(location, count, alloc);
  188. Log_keys(rand->log, "Random_bytes(%p) -> [%s]", (void*) rand, buf);
  189. Allocator_free(alloc);
  190. }
  191. return;
  192. }
  193. uint8_t* loc0 = location;
  194. uint64_t c0 = count;
  195. for (;;) {
  196. uintptr_t sz = randomCopy(rand, location, count);
  197. location += sz;
  198. count -= sz;
  199. if (count == 0) {
  200. if (Defined(Log_KEYS)) {
  201. struct Allocator* alloc = Allocator_child(rand->alloc);
  202. char* buf = Hex_print(loc0, c0, alloc);
  203. Log_keys(rand->log, "Random_bytes(%p) -> [%s]", (void*) rand, buf);
  204. Allocator_free(alloc);
  205. }
  206. return;
  207. }
  208. stir(rand);
  209. }
  210. }
  211. void Random_bytes_fromRust(Random_t* rand, uint8_t* location, uint64_t count)
  212. {
  213. Random_bytes(rand, location, count);
  214. }
  215. void Random_base32(struct Random* rand, uint8_t* output, uint32_t length)
  216. {
  217. Identity_check(rand);
  218. uint64_t index = 0;
  219. for (;;) {
  220. uint8_t bin[16];
  221. Random_bytes(rand, bin, 16);
  222. int ret = Base32_encode(&output[index], length - index, (uint8_t*)bin, 16);
  223. if (ret == Base32_TOO_BIG || index + ret == length) {
  224. break;
  225. }
  226. index += ret;
  227. }
  228. output[length - 1] = '\0';
  229. }
  230. Err_DEFUN Random_newWithSeed(
  231. struct Random** out,
  232. struct Allocator* alloc,
  233. struct Log* logger,
  234. RandomSeed_t* seed)
  235. {
  236. union Random_SeedGen* seedGen = Allocator_calloc(alloc, sizeof(union Random_SeedGen), 1);
  237. if (RandomSeed_get(seed, seedGen->buff)) {
  238. Err_raise(alloc, "Unable to initialize secure random number generator");
  239. }
  240. struct Random* rand = Allocator_calloc(alloc, sizeof(struct Random), 1);
  241. rand->seedGen = seedGen;
  242. rand->seed = seed;
  243. rand->nextByte = BUFFSIZE;
  244. rand->alloc = alloc;
  245. rand->log = logger;
  246. Identity_set(rand);
  247. rand->addRandomCounter = 255;
  248. Random_addRandom(rand, 0);
  249. stir(rand);
  250. *out = rand;
  251. return NULL;
  252. }
  253. Err_DEFUN Random_new(struct Random** out, struct Allocator* alloc, struct Log* logger)
  254. {
  255. RandomSeed_t* rs = SystemRandomSeed_new(NULL, 0, logger, alloc);
  256. return Random_newWithSeed(out, alloc, logger, rs);
  257. }