1use std::fmt::Write;
23
24#[must_use]
31pub(crate) fn format(blob: &[u8], crc: u16) -> String
32{
33 let mut out = String::with_capacity(8192);
34
35 let _ = writeln!(out, "ATECC608B configuration zone, 128 bytes");
36 let _ = writeln!(out, "========================================");
37 let _ = writeln!(out);
38 let _ = writeln!(out, "Bytes 0-15 are the read-only factory area. The chip ignores any");
39 let _ = writeln!(out, "Write command targeted at this range. The values shown here are");
40 let _ = writeln!(out, "placeholders from the generator and do not reflect what is actually");
41 let _ = writeln!(out, "stored in the chip.");
42 let _ = writeln!(out);
43
44 write_factory_area(&mut out, &blob[0..16]);
45 let _ = writeln!(out);
46
47 write_device_config(&mut out, &blob[16..20]);
48 let _ = writeln!(out);
49
50 write_slot_configs(&mut out, &blob[20..52]);
51 let _ = writeln!(out);
52
53 write_counters(&mut out, &blob[52..68]);
54 let _ = writeln!(out);
55
56 write_feature_area(&mut out, &blob[68..84]);
57 let _ = writeln!(out);
58
59 write_lock_area(&mut out, &blob[84..96]);
60 let _ = writeln!(out);
61
62 write_key_configs(&mut out, &blob[96..128]);
63 let _ = writeln!(out);
64
65 write_summary(&mut out, blob, crc);
66 out
67}
68
69fn write_factory_area(out: &mut String, bytes: &[u8])
70{
71 let _ = writeln!(out, "Factory area (bytes 0-15, read-only):");
72 let _ = writeln!(out, " SN[0..4] (0-3): {}", hex_row(&bytes[0..4]));
73 let _ = writeln!(out, " RevNum (4-7): {}", hex_row(&bytes[4..8]));
74 let _ = writeln!(out, " SN[4..8] (8-11): {}", hex_row(&bytes[8..12]));
75 let _ = writeln!(out, " SN[8] (12): {:02X} (must be 0xEE on real chip)", bytes[12]);
76 let _ = writeln!(out, " AES_Enable (13): {:02X}", bytes[13]);
77 let _ = writeln!(out, " I2C_Enable (14): {:02X}", bytes[14]);
78 let _ = writeln!(out, " Reserved1 (15): {:02X}", bytes[15]);
79}
80
81fn write_device_config(out: &mut String, bytes: &[u8])
82{
83 let _ = writeln!(out, "Device configuration (bytes 16-19):");
84 let _ = writeln!(out, " I2C_Address (16): 0x{:02X} (7-bit addr 0x{:02X} in 8-bit form)",
85 bytes[0], bytes[0] >> 1);
86 let _ = writeln!(out, " Reserved2 (17): 0x{:02X} (must be 0x00)", bytes[1]);
87 let _ = writeln!(out, " CountMatch (18): 0x{:02X} (feature disabled)", bytes[2]);
88 let _ = writeln!(out, " ChipMode (19): 0x{:02X} ({})",
89 bytes[3], decode_chip_mode(bytes[3]));
90}
91
92fn write_slot_configs(out: &mut String, bytes: &[u8])
93{
94 let _ = writeln!(out, "SlotConfig[16] (bytes 20-51), 2 bytes per slot, little-endian:");
95 for slot in 0..16
96 {
97 let lo = bytes[2 * slot];
98 let hi = bytes[2 * slot + 1];
99 let word = u16::from(lo) | (u16::from(hi) << 8);
100 let _ = writeln!(out,
101 " Slot {slot:2}: bytes {lo:02X} {hi:02X} -> 0x{word:04X} {}",
102 describe_slot_config(slot as u8, word),
103 );
104 }
105}
106
107fn write_counters(out: &mut String, bytes: &[u8])
108{
109 let _ = writeln!(out, "Counters (bytes 52-67):");
110 let _ = writeln!(out, " Counter0 (52-59): {}", hex_row(&bytes[0..8]));
111 let _ = writeln!(out, " Counter1 (60-67): {}", hex_row(&bytes[8..16]));
112 let _ = writeln!(out, " Note: 0xFF... means factory-default 'counter at zero'.");
113}
114
115fn write_feature_area(out: &mut String, bytes: &[u8])
116{
117 let _ = writeln!(out, "Feature configuration (bytes 68-83):");
118 let _ = writeln!(out, " UseLock (68): 0x{:02X}", bytes[0]);
119 let _ = writeln!(out, " VolatileKeyPermission (69): 0x{:02X}", bytes[1]);
120 let _ = writeln!(out, " SecureBoot (70-71): {} {}",
121 hex_byte(bytes[2]), hex_byte(bytes[3]));
122 let _ = writeln!(out, " KdflvLoc (72): 0x{:02X}", bytes[4]);
123 let _ = writeln!(out, " KdflvStr (73-74): {} {}",
124 hex_byte(bytes[5]), hex_byte(bytes[6]));
125 let _ = writeln!(out, " Reserved3 (75-83): {}", hex_row(&bytes[7..16]));
126}
127
128fn write_lock_area(out: &mut String, bytes: &[u8])
129{
130 let _ = writeln!(out, "Lock and chip-option area (bytes 84-95):");
131 let _ = writeln!(out, " UserExtra (84): 0x{:02X}", bytes[0]);
132 let _ = writeln!(out, " UserExtraAdd (85): 0x{:02X}", bytes[1]);
133 let _ = writeln!(out, " LockValue (data) (86): 0x{:02X} ({})",
134 bytes[2], if bytes[2] == 0x55 { "unlocked" } else { "locked" });
135 let _ = writeln!(out, " LockConfig (config) (87): 0x{:02X} ({})",
136 bytes[3], if bytes[3] == 0x55 { "unlocked" } else { "locked" });
137 let _ = writeln!(out, " SlotLocked bitmap (88-89): {} {}",
138 hex_byte(bytes[4]), hex_byte(bytes[5]));
139 let _ = writeln!(out, " ChipOptions (90-91): {} {}",
140 hex_byte(bytes[6]), hex_byte(bytes[7]));
141 let _ = writeln!(out, " X509format (92-95): {} {} {} {}",
142 hex_byte(bytes[8]), hex_byte(bytes[9]), hex_byte(bytes[10]), hex_byte(bytes[11]));
143}
144
145fn write_key_configs(out: &mut String, bytes: &[u8])
146{
147 let _ = writeln!(out, "KeyConfig[16] (bytes 96-127), 2 bytes per slot, little-endian:");
148 for slot in 0..16
149 {
150 let lo = bytes[2 * slot];
151 let hi = bytes[2 * slot + 1];
152 let word = u16::from(lo) | (u16::from(hi) << 8);
153 let _ = writeln!
154 ( out,
155 " Slot {slot:2}: bytes {lo:02X} {hi:02X} -> 0x{word:04X} {}",
156 describe_key_config(word),
157 );
158 }
159}
160
161fn write_summary(out: &mut String, blob: &[u8], crc: u16)
162{
163 let _ = writeln!(out, "Summary:");
164 let _ = writeln!(out, " Total bytes: {}", blob.len());
165 let _ = writeln!(out, " Writable portion (16-127): {} bytes", blob.len() - 16);
166 let _ = writeln!(out, " CRC-16 of writable portion: 0x{crc:04X}");
167 let _ = writeln!(out);
168 let _ = writeln!(out, "Lock the configuration zone only after verifying that the chip");
169 let _ = writeln!(out, "contains the same bytes 16-127 as above, and only with a host");
170 let _ = writeln!(out, "command that passes the expected CRC 0x{crc:04X} as a safety check.");
171}
172
173fn decode_chip_mode(byte: u8) -> String
174{
175 let i2c_extra = byte & 1;
176 let ttl = (byte >> 1) & 1;
177 let wdg_long = (byte >> 2) & 1;
178 let clk_div = (byte >> 3) & 0x1F;
179
180 let clock = match clk_div
181 {
182 0x00 => "M0",
183 0x05 => "M1",
184 0x0D => "M2",
185 _ => "unknown",
186 };
187 let watchdog = if wdg_long == 1 { "long" } else { "short" };
188
189 format!
190 (
191 "I2C_Extra={i2c_extra}, TTL={ttl}, watchdog={watchdog}, clock_divider={clock}",
192 )
193}
194
195fn describe_slot_config(slot: u8, word: u16) -> String
196{
197 let ext_sig = word & 1;
201 let is_secret = (word >> 7) & 1;
202 let gen_key = (word >> 8) & 1;
203 let priv_write = (word >> 9) & 1;
204 let write_config= (word >> 12) & 0xF;
205 let limited_use = (word >> 5) & 1;
206 let read_key = word & 0xF;
207 let write_key = (word >> 8) & 0xF;
208
209 let wc = match write_config
210 {
211 0x0 => "Always",
212 0x2 => "Never",
213 0x4 => "Always_then_Encrypt",
214 0x6 => "Encrypt",
215 0x8 => "Never (alt)",
216 0xC => "Never_then_Encrypt",
217 _ => "reserved",
218 };
219
220 match slot
221 {
222 5 | 6 => format!
223 (
224 "(data) ReadKey={read_key}, LimitedUse={limited_use}, IsSecret={is_secret}, WriteKey={write_key}, WriteConfig={wc}",
225 ),
226 8 => format!
227 (
228 "(I/O master key) IsSecret={is_secret}, WriteConfig={wc}",
229 ),
230 _ => format!
231 (
232 "(ECC) ExtSig={ext_sig}, IsSecret={is_secret}, GenKey={gen_key}, PrivWrite={priv_write}, WriteConfig={wc}",
233 ),
234 }
235}
236
237fn describe_key_config(word: u16) -> String
238{
239 let private = word & 1;
240 let pub_info = (word >> 1) & 1;
241 let key_type = (word >> 2) & 0x07;
242 let lockable = (word >> 5) & 1;
243 let req_auth = (word >> 7) & 1;
244 let auth_key = (word >> 8) & 0xF;
245
246 let kt = match key_type
247 {
248 0 => "B283",
249 1 => "K283",
250 4 => "P-256",
251 6 => "AES",
252 7 => "Data 32B",
253 _ => "unknown",
254 };
255
256 if private == 1
257 {
258 format!
259 (
260 "Private={private}, PubInfo={pub_info}, KeyType={kt}, Lockable={lockable}, ReqAuth={req_auth}, AuthKey={auth_key}",
261 )
262 }
263 else
264 {
265 format!
266 (
267 "Private={private}, KeyType={kt}, Lockable={lockable}",
268 )
269 }
270}
271
272fn hex_row(bytes: &[u8]) -> String
273{
274 bytes.iter().map(|b| format!("{b:02X}")).collect::<Vec<_>>().join(" ")
275}
276
277fn hex_byte(byte: u8) -> String
278{
279 format!("{byte:02X}")
280}
281
282#[cfg(test)]
283mod tests
284{
285 use super::*;
286 use crate::blob::build;
287 use crate::crc::crc16;
288
289 #[test]
290 fn annotation_runs_without_panicking()
291 {
292 let blob = build();
293 let crc = crc16(&blob[16..128]);
294 let text = format(&blob, crc);
295 assert!(text.contains("0xC92D"));
296 assert!(text.contains("Slot 0"));
297 assert!(text.contains("Slot 15"));
298 }
299
300 #[test]
301 fn slot_0_annotation_mentions_genkey_only()
302 {
303 let blob = build();
304 let crc = crc16(&blob[16..128]);
305 let text = format(&blob, crc);
306 let slot0_line = text
308 .lines()
309 .find(|line| line.contains("Slot 0:") && line.contains("ECC"))
310 .expect("slot 0 line must be present");
311 assert!(slot0_line.contains("GenKey=1"));
312 assert!(slot0_line.contains("PrivWrite=0"));
313 }
314
315 #[test]
316 fn slot_2_annotation_mentions_privwrite_on()
317 {
318 let blob = build();
319 let crc = crc16(&blob[16..128]);
320 let text = format(&blob, crc);
321 let slot2_line = text
322 .lines()
323 .find(|line| line.contains("Slot 2:") && line.contains("ECC"))
324 .expect("slot 2 line must be present");
325 assert!(slot2_line.contains("GenKey=1"));
326 assert!(slot2_line.contains("PrivWrite=1"));
327 }
328
329 #[test]
330 fn slot_5_annotation_labelled_as_data()
331 {
332 let blob = build();
333 let crc = crc16(&blob[16..128]);
334 let text = format(&blob, crc);
335 let slot5_line = text
336 .lines()
337 .find(|line| line.starts_with(" Slot 5:"))
338 .expect("slot 5 line must be present");
339 assert!(slot5_line.contains("(data)"));
340 assert!(slot5_line.contains("LimitedUse=1"));
341 }
342}