entropy.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #include <assert.h>
  2. #include <errno.h>
  3. #include <fcntl.h>
  4. #include <limits.h>
  5. #include <stdint.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include "warnp.h"
  9. #include "entropy.h"
  10. /**
  11. * XXX Portability
  12. * XXX We obtain random bytes from the operating system by opening
  13. * XXX /dev/urandom and reading them from that device; this works on
  14. * XXX modern UNIX-like operating systems but not on systems like
  15. * XXX win32 where there is no concept of /dev/urandom.
  16. */
  17. /**
  18. * Entropy reader state. At present it holds a file descriptor for
  19. * /dev/urandom, but in the future this structure may gain other OS-dependent
  20. * state, e.g. a Windows Handle.
  21. */
  22. struct entropy_read_cookie {
  23. int fd;
  24. };
  25. /**
  26. * entropy_read_init(void):
  27. * Initialize the ability to produce random bytes from the operating system,
  28. * and return a cookie.
  29. */
  30. struct entropy_read_cookie *
  31. entropy_read_init(void)
  32. {
  33. struct entropy_read_cookie * er;
  34. /* Allocate cookie. */
  35. if ((er = malloc(sizeof(struct entropy_read_cookie))) == NULL) {
  36. warnp("malloc");
  37. goto err0;
  38. }
  39. /* Open /dev/urandom. */
  40. if ((er->fd = open("/dev/urandom", O_RDONLY)) == -1) {
  41. warnp("open(/dev/urandom)");
  42. goto err1;
  43. }
  44. /* Success! */
  45. return (er);
  46. err1:
  47. free(er);
  48. err0:
  49. /* Failure! */
  50. return (NULL);
  51. }
  52. /**
  53. * entropy_read_fill(er, buf, buflen):
  54. * Fill the given buffer with random bytes provided by the operating system
  55. * using the resources in ${er}.
  56. */
  57. int
  58. entropy_read_fill(struct entropy_read_cookie * er, uint8_t * buf,
  59. size_t buflen)
  60. {
  61. ssize_t lenread;
  62. /* Sanity checks. */
  63. assert(er != NULL);
  64. assert(buflen <= SSIZE_MAX);
  65. /* Read bytes until we have filled the buffer. */
  66. while (buflen > 0) {
  67. if ((lenread = read(er->fd, buf, buflen)) == -1) {
  68. warnp("read(/dev/urandom)");
  69. goto err0;
  70. }
  71. /* The random device should never EOF. */
  72. if (lenread == 0) {
  73. warn0("EOF on /dev/urandom?");
  74. goto err0;
  75. }
  76. /* We've filled a portion of the buffer. */
  77. buf += (size_t)lenread;
  78. buflen -= (size_t)lenread;
  79. }
  80. /* Success! */
  81. return (0);
  82. err0:
  83. /* Failure! */
  84. return (-1);
  85. }
  86. /**
  87. * entropy_read_done(er):
  88. * Release any resources used by ${er}.
  89. */
  90. int
  91. entropy_read_done(struct entropy_read_cookie * er)
  92. {
  93. /* Sanity check. */
  94. assert(er != NULL);
  95. /* Close the device. */
  96. while (close(er->fd) == -1) {
  97. if (errno != EINTR) {
  98. warnp("close(/dev/urandom)");
  99. goto err1;
  100. }
  101. }
  102. /* Clean up. */
  103. free(er);
  104. /* Success! */
  105. return (0);
  106. err1:
  107. free(er);
  108. /* Failure! */
  109. return (-1);
  110. }
  111. /**
  112. * entropy_read(buf, buflen):
  113. * Fill the given buffer with random bytes provided by the operating system.
  114. */
  115. int
  116. entropy_read(uint8_t * buf, size_t buflen)
  117. {
  118. struct entropy_read_cookie * er;
  119. /* Sanity-check the buffer size. */
  120. assert(buflen <= SSIZE_MAX);
  121. /* Open /dev/urandom. */
  122. if ((er = entropy_read_init()) == NULL) {
  123. warn0("entropy_read_init");
  124. goto err0;
  125. }
  126. /* Read bytes until we have filled the buffer. */
  127. if (entropy_read_fill(er, buf, buflen)) {
  128. warn0("entropy_read_fill");
  129. goto err1;
  130. }
  131. /* Close the device. */
  132. if (entropy_read_done(er)) {
  133. warn0("entropy_read_done");
  134. goto err0;
  135. }
  136. /* Success! */
  137. return (0);
  138. err1:
  139. entropy_read_done(er);
  140. err0:
  141. /* Failure! */
  142. return (-1);
  143. }