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}