atecc608b/command/gendig.rs
1// Copyright (c) 2026 Tuloup Simon
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16//! `GenDig` command.
17//!
18//! Computes a digest combining the contents of a slot, the OTP, or the
19//! config zone with the current `TempKey` and stores the result back in
20//! `TempKey`. The result is what subsequent commands like an encrypted
21//! `Write` or `PrivWrite` use as the shared secret with the host.
22//!
23//! In this project's provisioning flow:
24//!
25//! 1. The host calls [`crate::AteccChannel::nonce_random`] to
26//! establish a shared `TempKey` value between host and chip.
27//! 2. The host calls [`AteccChannel::gendig`] with the I/O Protection Key slot
28//! (slot 8). The chip computes
29//! `SHA256(IOKey || OpCode || Mode || KeyId || SN || padding || TempKey)`
30//! and replaces `TempKey` with the result. The host computes the same
31//! digest off-chip.
32//! 3. The host XORs the new `TempKey` with the plaintext to write,
33//! appends a MAC, and sends an encrypted `Write` or `PrivWrite`.
34//!
35//! The driver does not orchestrate the host-side digest derivation: that is
36//! a service-layer concern.
37//!
38//! Reference: `CryptoAuthLib` `lib/calib/calib_gendig.c`, constants
39//! `GENDIG_ZONE_CONFIG` (0x00), `GENDIG_ZONE_OTP` (0x01),
40//! `GENDIG_ZONE_DATA` (0x02).
41
42use crate::driver::AteccChannel;
43use crate::error::AteccError;
44use crate::hal::AteccHal;
45use crate::opcodes::{EXEC_TIME_GENDIG_MS, OP_GENDIG};
46
47/// `param1` zone bits.
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum GenDigZone
51{
52 /// Pull source data from the config zone.
53 Config,
54 /// Pull source data from the OTP zone.
55 Otp,
56 /// Pull source data from a data slot.
57 Data,
58}
59
60impl GenDigZone
61{
62 /// Encode the zone as the low bits of `param1`.
63 const fn as_param1(self) -> u8
64 {
65 match self
66 {
67 GenDigZone::Config => 0x00,
68 GenDigZone::Otp => 0x01,
69 GenDigZone::Data => 0x02,
70 }
71 }
72}
73
74impl<H> AteccChannel<'_, H>
75where
76 H: AteccHal,
77{
78 /// Run a basic `GenDig` against a zone and key id, with no extra data.
79 ///
80 /// Mostly used to derive a shared digest from the I/O Protection Key
81 /// (slot 8 in this project) for subsequent encrypted writes.
82 ///
83 /// The chip responds with a status-only frame on success.
84 ///
85 /// # Errors
86 /// See [`AteccChannel::execute_command_status`].
87 pub async fn gendig
88 (
89 &mut self,
90 zone: GenDigZone,
91 key_id: u16,
92 ) -> Result<(), AteccError<H::Error>>
93 {
94 self.execute_command_status
95 (
96 OP_GENDIG,
97 zone.as_param1(),
98 key_id,
99 &[],
100 EXEC_TIME_GENDIG_MS,
101 )
102 .await
103 }
104}
105
106#[cfg(test)]
107mod tests
108{
109 use super::*;
110
111 #[test]
112 fn zone_encoding_matches_cryptoauthlib_constants()
113 {
114 assert_eq!(GenDigZone::Config.as_param1(), 0x00);
115 assert_eq!(GenDigZone::Otp.as_param1(), 0x01);
116 assert_eq!(GenDigZone::Data.as_param1(), 0x02);
117 }
118}