atecc608b/command/verify.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//! `Verify` command.
17//!
18//! Verifies an ECDSA P-256 signature on-chip.
19//!
20//! The driver exposes the "External" verify mode only: the host provides the
21//! 64-byte public key (uncompressed `X || Y`), the 64-byte signature
22//! (`R || S`), and the 32-byte digest is taken from `TempKey` (loaded via
23//! [`AteccChannel::nonce_passthrough`]). The "Stored" mode (verify against a
24//! public key already in a slot) and "Validate" mode (chained validation
25//! of a previously-stored signature) are not used in this project's
26//! workflow.
27//!
28//! Reference: `CryptoAuthLib` `lib/calib/calib_verify.c`, constants
29//! `VERIFY_MODE_EXTERNAL` (0x02), `VERIFY_KEY_P256` (0x0004).
30
31use crate::driver::AteccChannel;
32use crate::error::AteccError;
33use crate::hal::AteccHal;
34use crate::opcodes::{EXEC_TIME_VERIFY_MS, OP_VERIFY};
35
36use crate::command::genkey::PUBLIC_KEY_SIZE;
37use crate::command::sign::SIGNATURE_SIZE;
38
39/// Size of the `Verify External` data payload (signature || pubkey).
40pub(crate) const VERIFY_EXTERNAL_DATA_SIZE: usize = SIGNATURE_SIZE + PUBLIC_KEY_SIZE;
41
42/// `param1` mode: External verify.
43const VERIFY_MODE_EXTERNAL: u8 = 0x02;
44
45/// `param2` key id for the P-256 (NIST secp256r1) curve.
46const VERIFY_KEY_P256: u16 = 0x0004;
47
48impl<H> AteccChannel<'_, H>
49where
50 H: AteccHal,
51{
52 /// Verify an ECDSA P-256 signature against the digest currently loaded
53 /// in `TempKey`.
54 ///
55 /// Callers must load the digest via [`AteccChannel::nonce_passthrough`]
56 /// with target [`crate::command::nonce::NonceTarget::TempKey`]
57 /// immediately before this call.
58 ///
59 /// `signature` is the 64-byte raw `R || S` returned by `Sign`.
60 /// `public_key` is the 64-byte raw `X || Y` returned by `GenKey`.
61 ///
62 /// On success returns `Ok(true)` when the signature matches the digest
63 /// under the given public key, and `Ok(false)` when the chip cleanly
64 /// rejects the signature. Any other error condition surfaces as
65 /// [`AteccError`].
66 ///
67 /// # Errors
68 /// See [`AteccChannel::execute_command_status`]. A signature mismatch
69 /// surfaces here as `Ok(false)`, not as an error: the chip uses
70 /// [`crate::error::ChipError::CheckMacOrVerifyFailed`] (status 0x01)
71 /// specifically to flag this case, and the driver maps it back to a
72 /// boolean for ergonomics.
73 pub async fn verify_external
74 (
75 &mut self,
76 signature: &[u8; SIGNATURE_SIZE],
77 public_key: &[u8; PUBLIC_KEY_SIZE],
78 ) -> Result<bool, AteccError<H::Error>>
79 {
80 let mut data = [0u8; VERIFY_EXTERNAL_DATA_SIZE];
81 data[..SIGNATURE_SIZE].copy_from_slice(signature);
82 data[SIGNATURE_SIZE..].copy_from_slice(public_key);
83
84 let result = self
85 .execute_command_status
86 (
87 OP_VERIFY,
88 VERIFY_MODE_EXTERNAL,
89 VERIFY_KEY_P256,
90 &data,
91 EXEC_TIME_VERIFY_MS,
92 )
93 .await;
94
95 match result
96 {
97 Ok(()) => Ok(true),
98 Err(AteccError::Chip(crate::error::ChipError::CheckMacOrVerifyFailed)) =>
99 {
100 Ok(false)
101 }
102 Err(other) => Err(other),
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests
109{
110 use super::*;
111
112 #[test]
113 fn external_mode_matches_cryptoauthlib_constant()
114 {
115 assert_eq!(VERIFY_MODE_EXTERNAL, 0x02);
116 }
117
118 #[test]
119 fn p256_key_id_matches_cryptoauthlib_constant()
120 {
121 assert_eq!(VERIFY_KEY_P256, 0x0004);
122 }
123
124 #[test]
125 fn external_data_size_is_128()
126 {
127 assert_eq!(VERIFY_EXTERNAL_DATA_SIZE, 128);
128 }
129}