Skip to main content

atecc608b/command/
genkey.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//! `GenKey` command.
17//!
18//! Generates a P-256 ECC key pair inside a slot, or computes the public key
19//! corresponding to a private key already stored in a slot.
20//!
21//! Two operating modes are exposed:
22//!
23//! - [`AteccChannel::genkey_create`]: instruct the chip to generate a new P-256
24//!   private key entirely on-chip in the target slot. The private key never
25//!   leaves the device. The corresponding 64-byte public key is returned.
26//!   Subject to `KeyConfig.Private` and the data zone lock state.
27//!
28//! - [`AteccChannel::genkey_public`]: compute and output the public key
29//!   corresponding to the private key already stored in the target slot.
30//!   Read-only operation, useful at boot to retrieve the chip's identity
31//!   without re-generating the private key.
32//!
33//! Reference: `CryptoAuthLib` `lib/calib/calib_genkey.c`, constants
34//! `GENKEY_MODE_NEW_PRIVATE` (0x04), `GENKEY_MODE_PUBLIC` (0x00).
35//!
36//! # Public key format
37//!
38//! The returned 64 bytes are the uncompressed P-256 public key encoded as
39//! `X || Y`, each coordinate big-endian and 32 bytes wide. To produce the
40//! SEC1 uncompressed form expected by most libraries (including `p256`),
41//! prepend the `0x04` octet:
42//!
43//! ```text
44//! sec1 = [0x04] || X || Y
45//! ```
46
47use crate::driver::AteccChannel;
48use crate::error::AteccError;
49use crate::hal::AteccHal;
50use crate::opcodes::{EXEC_TIME_GENKEY_MS, OP_GENKEY};
51use crate::slot::Slot;
52
53/// Size of the returned public key in bytes (X || Y, raw P-256).
54pub const PUBLIC_KEY_SIZE: usize = 64;
55
56/// `param1` mode bits: generate a brand new private key inside the slot.
57const GENKEY_MODE_CREATE: u8 = 0x04;
58
59/// `param1` mode bits: only output the public key for the existing private
60/// key in the slot.
61const GENKEY_MODE_PUBLIC: u8 = 0x00;
62
63impl<H> AteccChannel<'_, H>
64where
65    H: AteccHal,
66{
67    /// Generate a new P-256 private key inside the target slot.
68    ///
69    /// The private key is created and stored entirely on-chip. The 64-byte
70    /// public key (uncompressed `X || Y`) is returned.
71    ///
72    /// # Errors
73    /// See [`AteccChannel::execute_command`]. Common chip errors include
74    /// attempting to write a slot configured `KeyConfig.Private = 0` or
75    /// attempting to regenerate a slot whose `SlotConfig.WriteConfig`
76    /// forbids it after data zone lock.
77    pub async fn genkey_create
78    (
79        &mut self,
80        slot: Slot,
81    ) -> Result<[u8; PUBLIC_KEY_SIZE], AteccError<H::Error>>
82    {
83        self.genkey_internal(GENKEY_MODE_CREATE, slot).await
84    }
85
86    /// Compute and return the public key for the private key already stored
87    /// in the target slot. Does not modify chip state.
88    ///
89    /// # Errors
90    /// See [`AteccChannel::execute_command`].
91    pub async fn genkey_public
92    (
93        &mut self,
94        slot: Slot,
95    ) -> Result<[u8; PUBLIC_KEY_SIZE], AteccError<H::Error>>
96    {
97        self.genkey_internal(GENKEY_MODE_PUBLIC, slot).await
98    }
99
100    async fn genkey_internal
101    (
102        &mut self,
103        mode: u8,
104        slot: Slot,
105    ) -> Result<[u8; PUBLIC_KEY_SIZE], AteccError<H::Error>>
106    {
107        // Response: count(1) + 64 pubkey + crc(2) = 67 bytes.
108        let mut response_buf = [0u8; 1 + PUBLIC_KEY_SIZE + 2];
109        let param2 = u16::from(slot.as_u8());
110        let payload = self
111            .execute_command
112            (
113                OP_GENKEY,
114                mode,
115                param2,
116                &[],
117                EXEC_TIME_GENKEY_MS,
118                &mut response_buf,
119            )
120            .await?;
121
122        let bytes: &[u8; PUBLIC_KEY_SIZE] = payload
123            .try_into()
124            .map_err(|_| AteccError::MalformedResponse)?;
125        Ok(*bytes)
126    }
127}
128
129#[cfg(test)]
130mod tests
131{
132    use super::*;
133
134    #[test]
135    fn modes_match_cryptoauthlib_constants()
136    {
137        assert_eq!(GENKEY_MODE_CREATE, 0x04);
138        assert_eq!(GENKEY_MODE_PUBLIC, 0x00);
139    }
140
141    #[test]
142    fn public_key_size_is_64()
143    {
144        assert_eq!(PUBLIC_KEY_SIZE, 64);
145    }
146}