Skip to main content

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}