The blog.
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.
 
 
 
 

298 lines
15 KiB

  1. ---
  2. title: Nearly Complete Guide to RNG on a microcontroller
  3. description: >
  4. How to initialize and run an RNG on an STM32L151CC microcontroller.
  5. created: !!timestamp '2021-05-18'
  6. listable: false
  7. time: 12:00 PM
  8. tags:
  9. - security
  10. - rng
  11. - microcontroller
  12. ---
  13. Security depends upon cryptography and which in turn depends upon a
  14. Random Number Generator (RNG). An RNG is used for key generation (both
  15. symmetric and asymmetric) and key negotiation (session establishment).
  16. The later is an absolute requirement to ensure that communications can
  17. be secured. The former (key generation) can be used at first boot for
  18. personalization, but isn't necessary as it could be done when personalizing
  19. the device at programming or first deployment.
  20. There are two types of RNGs, the first is a True Random Number Generator
  21. (TRNG). This is one that takes some non-deterministic process, often
  22. physical, and measures it. Often, these are slow and are not uniform,
  23. requiring a post processing step before the are useful.
  24. The second type is a Pseudo Random Number Generator (PRNG)<label
  25. for="sn-drbg" class="margin-toggle sidenote-number"></label><input
  26. type="checkbox" id="sn-drbg" class="margin-toggle"/><span
  27. class="sidenote">[NIST](https://www.nist.gov/) also refers to a
  28. PRNG as a Deterministic Random Bit Generator (DRBG).</span>. PRNGs
  29. take a seed, and can generate large, effectively unlimited when seeded
  30. properly, amounts of random looking data from them. The issue is than
  31. if someone is able to obtain the seed, they will be able to predict
  32. the subsequent values, allowing breaking security.
  33. The standard practice is to gather data from a TRNG, and use it to seed
  34. a PRNG. It used to be common that the PRNG would be reseeded, but I
  35. agree w/ djb (D. J. Bernstein) that once seeded, no additional seeding
  36. is needed<label for="sn-entropy" class="margin-toggle sidenote-number"></label>
  37. <input type="checkbox" id="sn-entropy" class="margin-toggle"/>
  38. <span class="sidenote">See his blog post
  39. [Entropy Attacks!](https://blog.cr.yp.to/20140205-entropy.html)</span>
  40. as modern PRNGs are secure enough and can generate enough randomness
  41. that their state will not leak.
  42. There are lots of libraries and papers that talk about how to solve the
  43. problem for RNGs on a microcontroller that may not have an integrated
  44. [T]RNG block, but I have not been able to find a complete guide for
  45. integrating their work into a project where even a relative beginner
  46. could get it functional.
  47. This article was written as I developed the
  48. [lora-irrigation](https://www.funkthat.com/gitea/jmg/lora-irrigation)
  49. project. This project will be used as an example, and the code reference
  50. is mostly licensed under the 2-clause BSD license, and so is freely
  51. usable for your own projects.
  52. Sources of Randomness
  53. ---------------------
  54. As mentioned, most microcontrollers do not have a dedicated hardware
  55. block like modern AMD64 (aka x86-64) processors do w/ the RDRAND
  56. instruction. Though they do not, there are other sources that are
  57. available.
  58. The first, and easiest one is the Analog Digital Converter (ADC). Even
  59. if the ADC pin is tied to ground, the process of digital conversion is
  60. not 100% deterministic as there are errors in the converter or noise
  61. introduced on the pin.<label for="sn-adcnoise"
  62. class="margin-toggle sidenote-number"></label><input type="checkbox"
  63. id="sn-adcnoise" class="margin-toggle"/><span class="sidenote">The article
  64. [ADC Input Noise: The Good, The Bad, and The Ugly. Is No Noise Good
  65. Noise?](https://www.analog.com/en/analog-dialogue/articles/adc-input-noise.html)
  66. talks about this.</span>
  67. The data sheet for the microcontroller will help determine the expected
  68. randomness from the part. In the case of the
  69. [STM32L151CC](https://www.st.com/content/st_com/en/products/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus/stm32-ultra-low-power-mcus/stm32l1-series/stm32l151-152/stm32l151cc.html)
  70. that I'm using, Table 57 of the data sheet lists the Effective number
  71. of bits (ENOB) as typically 10 bits, which is a couple bits short of
  72. the 12 bit resolution of the ADC. This means that the 2 least
  73. significant bits are likely to have some noise in them. I did a run,
  74. and collected 114200 samples from the ADC. The [Shannon
  75. entropy](https://en.wikipedia.org/wiki/R%C3%A9nyi_entropy#Shannon_entropy)
  76. calculated using the empirical probabilities was 2.48.<label
  77. for="sn-shannonenropy" class="margin-toggle sidenote-number"></label>
  78. <input type="checkbox" id="sn-shannonenropy" class="margin-toggle"/>
  79. <span class="sidenote">Now this is not strictly Shannon entropy, as the
  80. values were calculated from the experiment, and Shannon entropy should
  81. be calculated from the a priori probabilities.</span> Discarding the
  82. 0's (which makes up over half the results) improves the entropy
  83. calculation to 3.29. The
  84. [min-entropy](https://en.wikipedia.org/wiki/R%C3%A9nyi_entropy#Min-entropy)<label for="sn-min-entropy-fwdref" class="margin-toggle sidenote-number"></label>,
  85. <input type="checkbox" id="sn-min-entropy-fwdref" class="margin-toggle"/>
  86. <span class="sidenote">Forward reference:
  87. <a href="#min-entropy-awk">min-entropy awk script</a></span>
  88. a better indicator of entropy, calculation is 1.2 bits, and if all the
  89. 0's are dropped, it improves to 2.943. This does help, but in the end,
  90. subtracting the data sheet's ENOB from the ADC resolution does result
  91. in an approximate estimate of entropy.
  92. It is possibly that a correlation analysis between samples could
  93. further reduce the entropy gathers via the ADC, but with sufficient
  94. collection, this should be able to be avoided.
  95. The second is using uninitialized SRAM. It turns out that this has
  96. been studied in [Software Only, Extremely Compact, Keccak-based Secure
  97. PRNG on ARM Cortex-M](https://dl.acm.org/doi/10.1145/2593069.2593218)
  98. and [Secure PRNG Seeding on Commercial Off-the-Shelf
  99. Microcontrollers](https://www.intrinsic-id.com/wp-content/uploads/2017/05/prng_seeding.pdf).
  100. Depending upon how the SRAM is designed in the chip, it can create a
  101. situation where each bit of SRAM will be indeterminate at boot up.
  102. Both of these papers studied a similar microcontroller, an
  103. STM32F100R8 to the one I am using, a STM32L151CC.
  104. I ran my own experiments where I powered on an STM3L151CC and dumped
  105. the SRAM 8 times and analyzed the results. I limited my analysis to
  106. 26863 bytes the 32 KiBytes of ram (remaining was data/bss or stack, so
  107. would not change, or was zeros). I then calculated the min-entropy for
  108. each bit across power cycles and the resulting sum was 11188, or
  109. approximately .416 bits per byte. This is 5.2% and in line with what
  110. the later paper observed for a similar device.
  111. Part of using a source of randomness is making sure that it is usable.
  112. In the case of the ADC, each reading can be evaluated against previous
  113. reads to ensure that the data being obtained is possibly random. In
  114. the case of SRAM, this is more tricky, as the state of SRAM is static,
  115. and short of a reset, will not change. This means that to use SRAM,
  116. proper analysis of the device, or family of devices, need to be evaluated
  117. for suitability. There are cases where a device's SRAM does not provide
  118. adequate entropy, as discussed in the papers, and so this method should
  119. not be used in those cases, or not solely relied upon.
  120. The following is an `awk` script for calculating the min-entropy of the
  121. provided data. Each sample must the first item on a line, and each sample
  122. must be a hexadecimal value w/o any leading `0x` or other leading
  123. identifier:
  124. <pre id="min-entropy-awk" class="language-awk fullwidth"><code># Copyright 2021 John-Mark Gurney
  125. # This script is licensed under the 2-clause BSD license
  126. function max(a, b)
  127. {
  128. if (a > b)
  129. return a;
  130. else
  131. return b;
  132. }
  133. {
  134. v = ("0x" $1) + 0; a[NR] = v;
  135. maxv = max(maxv, v);
  136. }
  137. END {
  138. tcnt = length(a);
  139. me = 0;
  140. for (bit = 0; 2^bit <= maxv; bit += 1) {
  141. cnt0 = 0;
  142. cnt1 = 0;
  143. for (i in a) {
  144. tbit = int((a[i] / 2 ^ bit) % 2);
  145. if (tbit)
  146. cnt1 += 1;
  147. else
  148. cnt0 += 1;
  149. }
  150. v = -log(max(cnt0, cnt1) / tcnt) / log(2);
  151. print "bit " bit ":\t" v;
  152. me += v;
  153. }
  154. printf "total:\t%0.3f\n", me;
  155. }
  156. </code></pre>
  157. It is also possible that there are other parts of the board/design
  158. that could be a source of randomness. The project that started this
  159. journey is using [LoRa](https://en.wikipedia.org/wiki/LoRa) for
  160. communication. It turns out that the sample code for the radio chip
  161. ([LoRaMac&#x2011;node](https://github.com/Lora-net/LoRaMac-node)) implements
  162. a [random interface](https://github.com/Lora-net/LoRaMac-node/blob/7f12997754ad8e38a84daa85f62e7e6c0e5dbe59/src/radio/radio.h#L154-L163).
  163. The function just waits one milisecond, reads the RSSI value, takes
  164. the low bit and repeats this 32 times to return a 32-bit word. There
  165. are issues with this as I cannot find any description of the expected
  166. randomness in the data sheet, nor in the code. It also does not do
  167. any conditioning, so just because it returns 32-bits, does not guarantee
  168. 32-bits of usable entropy. I have briefly looked at the output, and
  169. there does appear to be higher lengths of runs than expected. Another
  170. issue is that it's collection takes a while, as the fastest is 1 bit
  171. per ms. So, assuming the need to collect 8 bits for 1 bit of entropy
  172. (pure speculation), that means at minimum 2 seconds to collect the
  173. 2048 bits necessary for 256 bits of entropy.
  174. Uniquifying
  175. -----------
  176. One of the other ways to help ensure that a microcontroller is to
  177. integrate per device values into the PRNG. This does not guarantee
  178. uniqueness between boots, but it does make it harder to attack if an
  179. attacker is able to control the other sources of randomness.
  180. In the case of the STM32L151 chip I am using, there is a unique
  181. device id register. The device register is programmed at the
  182. factory. Because it is unknown if this unique id is recorded by the
  183. manufacturer, and possibly traced through the supply chain, and no
  184. guarantees are made to both the uniqueness or privacy, it has limited
  185. use to provide any serious additional randomization.
  186. Another method, is to write entropy at provisioning time. This can be
  187. done in either flash memory or EEPROM, which may have a more granular
  188. write access.
  189. Using SRAM
  190. ----------
  191. The tricky part of using SRAM is figuring out how to access the
  192. uninitialized memory. Despite having full access to the environment,
  193. modifying the startup code, which is often written in assembly, to do
  194. the harvesting makes an implementation less portable. Using standard
  195. C, or another high level language, makes this easier, *but* we need to
  196. know where the end of the data and bss segments are. This is where
  197. looking at the linker script will come in.
  198. A linker script is used to allocate and map the program's data to the
  199. correct locations. This includes allocating memory so that all the
  200. code and data fits in flash, but also allocating ram for variables, and
  201. stack. Often there will be a symbol provided that marks where the data
  202. and bss sections in ram end, and the heap should begin. For example,
  203. in [`STM32L151CCUX_FLASH.ld` at lines 185 &
  204. 186](https://www.funkthat.com/gitea/jmg/lora-irrigation/src/commit/91a6fb590b68af1bcd34f776d4a58c89ac581c7d/stm32/l151ccux/STM32L151CCUX_FLASH.ld#L185-L186)
  205. it defines the symbols `end` and `_end`, the later of which is often
  206. used by `sbrk` (or `_sbrk` in my project's case in
  207. libnosys<label for="sn-sbrk-sample" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-sbrk-sample" class="margin-toggle"/>
  208. <span class="sidenote">A sample `_sbrk` is in [utils_syscalls.c](https://www.funkthat.com/gitea/jmg/lora-irrigation/src/commit/91a6fb590b68af1bcd34f776d4a58c89ac581c7d/loramac/src/boards/mcu/saml21/hal/utils/src/utils_syscalls.c#L67-L83),
  209. though this particular implementation is not used by my project.</span>)
  210. to allocate memory for the heap. Using sbrk is the easiest method to
  211. access uninitalized SRAM, but modifying or adding a symbol can be used
  212. if your microcontroller's framework does not support sbrk.
  213. Putting it together
  214. -------------------
  215. It is accepted that integrating as many difference sournces of entropy
  216. (TRNGs) is best. This ensures that as long as any single soruce is
  217. good, or each one is not great, but combined they provide enough
  218. entropy (preferably at least 128 bits), that the seeded PRNG will be
  219. secure and unpredictable.
  220. As some sources are only available at first boot, e.g. SRAM, it is
  221. best to save a fork of the PRNG to stable storage. In my
  222. implementation, I decided to use EEPROM for this. I added an
  223. additional EEPROM section in the linker script, and then added a symbol
  224. [rng_save](https://www.funkthat.com/gitea/jmg/lora-irrigation/src/branch/main/strobe_rng_init.c#L39)
  225. that is put in this section. This should be 256-bits (32-bytes) as
  226. the savings of smaller does not make sense, and any proper PRNG when
  227. seeded with 256-bits will provide enough randomness. Writing to EEPROM
  228. does require a little more work to have the code save to this region,
  229. rather than RAM, but the STM32 HAL layer has functions that make this
  230. easy.
  231. It would be great if where the PRNG seed could be in read-once,
  232. write-once memory to ensure that it can be read, mixed in with any
  233. additional entropy, and the written out, but I do not know of any
  234. microcontroller that supports this feature.
  235. Part of this is is to ensure that the the state between the saved
  236. seed, and the PRNG state used for this boot is disjoint, and that if
  237. either seed is compromised, neither can be backtracked to obtain the
  238. other. In the case of [strobe](https://strobe.sourceforge.io/papers/strobe-latest.pdf),
  239. the function [strobe_randomize](https://www.funkthat.com/gitea/jmg/lora-irrigation/src/branch/main/strobe/strobe.c#L319-L331)
  240. does a RATCHET operation at the end, which ensure the state cannot be rolled
  241. back to figure out what was generated, and as the generated bytes does
  242. not contain the entire state of the PRNG, it cannot be used to
  243. reconstruct the future seed.
  244. Another advantage of using EEPROM is the ability to provide an initial
  245. set of entropy bytes at firmware flashing time. I did attempt to add
  246. this, but OpenOCD, which I use for programming the Node151 device,
  247. does not support programming EEPROM, so in my case, this was not
  248. possible<label for="sn-eeprom-flash" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-eeprom-flash" class="margin-toggle"/><span class="sidenote">Despite not using it, the infrastructure to generate perso entropy is still present in the [Makefile](https://www.funkthat.com/gitea/jmg/lora-irrigation/src/branch/main/Makefile#L152-L157).</span>.
  249. I could have added an additional source data file to the flash, but
  250. figured that the other sources of entropy were adequate enough for my
  251. project.
  252. {#
  253. Conclusion
  254. ----------
  255. Modern microcontrollers do have a number of sources of entropy that can
  256. be used. With a little bit of work, a PRNG seed can be saved between
  257. resets, allowing for more secure operation, and even preloading of
  258. entropy. #}