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}