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.
 
 
 
 

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