Skip to main content

hsm_crypto_service/
error.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//! Error type returned by the crypto service.
17
18use core::fmt::Debug;
19
20use atecc608b::{AteccError, AteccErrorKind, Slot};
21
22use crate::pin::FormatError;
23
24/// Error returned by [`crate::CryptoService`] methods.
25#[derive(Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub enum CryptoServiceError<HalError>
28where
29    HalError: Debug,
30{
31    /// Underlying driver error.
32    Atecc(AteccError<HalError>),
33
34    /// PIN or PUK format was rejected (wrong length or non-digit character).
35    InvalidFormat(FormatError),
36
37    /// PIN verification failed. Tries remaining counter is included.
38    PinIncorrect
39    {
40        /// Number of attempts the user has left before the slot is blocked.
41        tries_remaining: u8,
42    },
43
44    /// PIN slot is hardware-blocked. Only PUK reset can recover.
45    PinBlocked,
46
47    /// PUK was wrong. After [`crate::slots::PUK_MAX_RETRIES`] failures the
48    /// chip is bricked.
49    PukIncorrect
50    {
51        /// Number of PUK attempts the user has left before the chip is bricked.
52        tries_remaining: u8,
53    },
54
55    /// PUK retry count exhausted. The chip is permanently unusable.
56    Bricked,
57
58    /// A signing operation was requested but the PIN session is not active.
59    PinRequired,
60
61    /// The chip has not been provisioned yet (config zone is unlocked, or
62    /// SHA-256 hashes are not stored in the expected slots).
63    NotProvisioned,
64
65    /// The caller specified a slot index that is invalid for the
66    /// requested operation (out of policy, e.g. `provision_slot` on an
67    /// ECC slot, or `sign` on a slot not configured for ECC).
68    InvalidSlot
69    {
70        /// The slot the caller tried to use.
71        slot: Slot,
72    },
73
74    /// The caller asked for [`crate::CryptoService::emergency_reset`] but
75    /// the precondition (both PIN and PUK batches fully exhausted) is
76    /// not met. The user must use the normal recovery paths
77    /// (`unblock_pin`) instead.
78    EmergencyResetNotPermitted
79    {
80        /// Number of PIN attempts the user still has in the current batch.
81        pin_tries_remaining: u8,
82        /// Number of PUK attempts the user still has in the current batch.
83        puk_tries_remaining: u8,
84    },
85}
86
87impl<HalError> CryptoServiceError<HalError>
88where
89    HalError: Debug,
90{
91    /// Return the non-generic [`AteccErrorKind`] if this error wraps a
92    /// driver-level failure.
93    ///
94    /// Returns `None` for variants that originated above the driver layer
95    /// (PIN/PUK failures, format errors, session policy violations).
96    ///
97    /// Intended for layers that must serialize the error over a wire
98    /// format without naming the concrete `HalError` type, in particular
99    /// the firmware's USB-HID dispatcher.
100    #[must_use]
101    pub fn atecc_kind(&self) -> Option<AteccErrorKind>
102    {
103        match self
104        {
105            CryptoServiceError::Atecc(err) => Some(err.kind()),
106            _                                                     => None,
107        }
108    }
109}
110
111impl<HalError> From<AteccError<HalError>> for CryptoServiceError<HalError>
112where
113    HalError: Debug,
114{
115    fn from(err: AteccError<HalError>) -> Self
116    {
117        CryptoServiceError::Atecc(err)
118    }
119}
120
121impl<HalError> From<FormatError> for CryptoServiceError<HalError>
122where
123    HalError: Debug,
124{
125    fn from(err: FormatError) -> Self
126    {
127        CryptoServiceError::InvalidFormat(err)
128    }
129}