Skip to main content

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}