1#![no_std]
2
3use rand::RngCore;
4use stm32f4xx_hal::{
5 stm32::ADC1,
6 adc::{Adc, Temperature, Vref, config::Resolution},
7 signature::{Uid, VrefCal, VtempCal30},
8};
9use rand_chacha::{
10 ChaCha8Rng,
11 rand_core::SeedableRng,
12};
13pub use stm32f4xx_hal::adc::config::SampleTime;
14
15#[derive(Debug, Copy, Clone)]
17pub enum EntropySources {
18 TempOnly(SampleTime),
19 VrefOnly(SampleTime),
20 DevInfoAndTemp(SampleTime),
21 DevInfoAndVref(SampleTime),
22 TempAndVref(SampleTime),
23 AllSources(SampleTime),
24}
25
26#[derive(Debug, Copy, Clone)]
28pub struct RngConfig {
29 pub min_init_cycles: u32,
33
34 pub max_init_cycles: u32,
39
40 pub entropy_sources: EntropySources,
47}
48
49impl Default for RngConfig {
50 fn default() -> Self {
51 RngConfig {
52 min_init_cycles: 8192,
53 max_init_cycles: 16384,
54 entropy_sources: EntropySources::AllSources(SampleTime::Cycles_480),
55 }
56 }
57}
58
59fn fill_device_info(slice: &mut [u8; 16]) {
61 let uid = Uid::get();
62 let vtc30 = VtempCal30::get();
63 let vrc = VrefCal::get();
64
65 let lot = uid.lot_num().as_bytes();
67 slice[..7].copy_from_slice(lot);
68
69 let waf = uid.waf_num();
71 slice[7] = waf;
72
73 let xpos = uid.x().to_ne_bytes();
75 slice[8..10].copy_from_slice(&xpos);
76
77 let ypos = uid.y().to_ne_bytes();
79 slice[10..12].copy_from_slice(&ypos);
80
81 let cal30 = vtc30.read().to_ne_bytes();
83 slice[12..14].copy_from_slice(&cal30);
84
85 let vrcal = vrc.read().to_ne_bytes();
87 slice[14..16].copy_from_slice(&vrcal);
88}
89
90enum Source {
91 Temp,
92 Vref,
93}
94
95fn fill_adc_readings(adc: &mut Adc<ADC1>, source: Source, samples: SampleTime, bytes: &mut [u8]) {
96 for byte in bytes.iter_mut() {
99 *byte = match source {
101 Source::Temp => adc.convert(&Temperature, samples),
102 Source::Vref => adc.convert(&Vref, samples),
103 } as u8;
104 }
105}
106
107pub fn seed_rng(adc: &mut Adc<ADC1>, config: RngConfig) -> ChaCha8Rng {
109 adc.enable_temperature_and_vref();
110 adc.set_resolution(Resolution::Twelve);
111
112 let mut key = [0u8; 32];
113 let mut bytes = [0u8; 16];
114
115 match config.entropy_sources {
116 EntropySources::TempOnly(sample_time) => {
117 fill_adc_readings(adc, Source::Temp, sample_time, &mut key);
118 }
119 EntropySources::VrefOnly(sample_time) => {
120 fill_adc_readings(adc, Source::Vref, sample_time, &mut key);
121 }
122 EntropySources::DevInfoAndTemp(sample_time) => {
123 fill_device_info(&mut bytes);
124 key[..16].copy_from_slice(&bytes);
125 fill_adc_readings(adc, Source::Temp, sample_time, &mut key[16..]);
126 }
127 EntropySources::DevInfoAndVref(sample_time) => {
128 fill_device_info(&mut bytes);
129 key[..16].copy_from_slice(&bytes);
130 fill_adc_readings(adc, Source::Vref, sample_time, &mut key[16..]);
131 }
132 EntropySources::TempAndVref(sample_time) => {
133 fill_adc_readings(adc, Source::Temp, sample_time, &mut key[..16]);
134 fill_adc_readings(adc, Source::Vref, sample_time, &mut key[16..]);
135 }
136 EntropySources::AllSources(sample_time) => {
137 fill_device_info(&mut bytes);
138 key[..16].copy_from_slice(&bytes);
139 fill_adc_readings(adc, Source::Temp, sample_time, &mut key[16..24]);
140 fill_adc_readings(adc, Source::Vref, sample_time, &mut key[24..]);
141 }
142 }
143
144 let mut rng = ChaCha8Rng::from_seed(key);
145
146 let min = config.min_init_cycles.min(config.max_init_cycles);
149 let max = config.min_init_cycles.max(config.max_init_cycles);
150 let end = (max - min).min(1024);
151
152 let nop_cycles = min + (rng.next_u32() % end);
153
154 for _ in 0..nop_cycles {
155 let _ = rng.next_u32();
156 }
157
158 rng
159}