keycard/
error.rs

1// SPDX-FileCopyrightText: 2025 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use server::{AsScalar, FromScalar};
5
6#[derive(
7    Debug, Clone, Copy, thiserror::Error, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, PartialEq,
8)]
9pub enum KeycardError {
10    #[error("OS error: {:?}", xous::Error::from_usize(*.0))]
11    Xous(usize),
12    #[error("Security error: AccessDenied")]
13    Security(usize),
14    #[error("Shamir error: {0:?}")]
15    Shamir(crypto::error::ShamirError),
16    #[error("Crypto error: {0:?}")]
17    Crypto(crypto::error::CryptoError),
18    #[error("Nfc error: {0:?}")]
19    Nfc(nfc::error::NfcError),
20    #[error("Ndef error")]
21    Ndef,
22    #[error("No more shards left")]
23    NoShardLeft,
24    #[error("Not a magic backup shard")]
25    NotMagicBackupShard,
26    #[error("Invalid data")]
27    InvalidData,
28    #[error("Different hardware UID")]
29    DifferentDeviceId,
30    #[error("Seed missing")]
31    SeedMissing,
32    #[error("Different seed fingerprint")]
33    DifferentSeedFingerprint,
34    #[error("Not enough shards")]
35    NotEnoughShards,
36    #[error("HMAC mismatch")]
37    HmacMismatch,
38    #[error("Blank shard")]
39    BlankShard,
40    #[error("Blank tag")]
41    BlankTag,
42    #[error("Invalid shamir scheme")]
43    InvalidScheme,
44    #[error("Shard scheme doesn't match active scheme")]
45    SchemeMismatch,
46    #[error("Shard timestamp {found} doesn't match expected {expected}")]
47    TimestampMismatch { expected: u32, found: u32 },
48    #[error("Other error")]
49    Other,
50}
51
52#[derive(Clone, Copy, Debug, thiserror::Error, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
53pub enum KeycardIdentifyError {
54    #[error("Invalid data")]
55    InvalidData,
56    #[error("Different device ID")]
57    DifferentDeviceId,
58    #[error("Different seed fingerprint")]
59    DifferentSeedFingerprint,
60    #[error("Unauthenticated shard")]
61    HmacMismatch,
62    #[error("Existing shard")]
63    ExistingShard,
64}
65
66impl From<xous::Error> for KeycardError {
67    fn from(value: xous::Error) -> Self { KeycardError::Xous(value.to_usize()) }
68}
69
70impl From<security::AccessDenied> for KeycardError {
71    fn from(_: security::AccessDenied) -> Self { KeycardError::Security(0) }
72}
73
74impl From<security::GetDeviceIdError> for KeycardError {
75    fn from(_: security::GetDeviceIdError) -> Self { KeycardError::Security(1) }
76}
77
78impl From<crypto::error::ShamirError> for KeycardError {
79    fn from(value: crypto::error::ShamirError) -> Self { Self::Shamir(value) }
80}
81
82impl From<crypto::error::CryptoError> for KeycardError {
83    fn from(value: crypto::error::CryptoError) -> Self { Self::Crypto(value) }
84}
85
86impl From<nfc::error::NfcError> for KeycardError {
87    fn from(value: nfc::error::NfcError) -> Self { Self::Nfc(value) }
88}
89
90impl AsScalar<3> for KeycardError {
91    fn as_scalar(&self) -> [u32; 3] {
92        match self {
93            KeycardError::Xous(e) => [1, *e as u32, 0],
94            KeycardError::Security(e) => [2, *e as u32, 0],
95            KeycardError::Shamir(e) => [3, AsScalar::<1>::as_scalar(e)[0], 0],
96            KeycardError::Crypto(e) => [4, AsScalar::<1>::as_scalar(e)[0], 0],
97            KeycardError::Nfc(e) => [5, AsScalar::<1>::as_scalar(e)[0], 0],
98            KeycardError::Ndef => [6, 0, 0],
99            KeycardError::NoShardLeft => [7, 0, 0],
100            KeycardError::NotMagicBackupShard => [8, 0, 0],
101            KeycardError::InvalidData => [9, 0, 0],
102            KeycardError::DifferentDeviceId => [10, 0, 0],
103            KeycardError::DifferentSeedFingerprint => [11, 0, 0],
104            KeycardError::SeedMissing => [12, 0, 0],
105            KeycardError::NotEnoughShards => [13, 0, 0],
106            KeycardError::HmacMismatch => [14, 0, 0],
107            KeycardError::BlankShard => [15, 0, 0],
108            KeycardError::BlankTag => [16, 0, 0],
109            KeycardError::InvalidScheme => [17, 0, 0],
110            KeycardError::SchemeMismatch => [18, 0, 0],
111            KeycardError::TimestampMismatch { expected, found } => [19, *expected, *found],
112            KeycardError::Other => [0, 0, 0],
113        }
114    }
115}
116
117impl FromScalar<3> for KeycardError {
118    fn from_scalar(value: [u32; 3]) -> Self {
119        match value[0] {
120            1 => KeycardError::Xous(value[1] as usize),
121            2 => KeycardError::Security(value[1] as usize),
122            3 => KeycardError::Shamir(crypto::error::ShamirError::from_scalar([value[1]])),
123            4 => KeycardError::Crypto(crypto::error::CryptoError::from_scalar([value[1]])),
124            5 => KeycardError::Nfc(nfc::error::NfcError::from_scalar([value[1]])),
125            6 => KeycardError::Ndef,
126            7 => KeycardError::NoShardLeft,
127            8 => KeycardError::NotMagicBackupShard,
128            9 => KeycardError::InvalidData,
129            10 => KeycardError::DifferentDeviceId,
130            11 => KeycardError::DifferentSeedFingerprint,
131            12 => KeycardError::SeedMissing,
132            13 => KeycardError::NotEnoughShards,
133            14 => KeycardError::HmacMismatch,
134            15 => KeycardError::BlankShard,
135            16 => KeycardError::BlankTag,
136            17 => KeycardError::InvalidScheme,
137            18 => KeycardError::SchemeMismatch,
138            19 => KeycardError::TimestampMismatch { expected: value[1], found: value[2] },
139            _ => KeycardError::Other,
140        }
141    }
142}
143
144impl From<usize> for KeycardError {
145    fn from(value: usize) -> Self { Self::from_scalar([value as u32, 0, 0]) }
146}
147
148impl From<KeycardError> for usize {
149    fn from(value: KeycardError) -> Self { server::AsScalar::<3>::as_scalar(&value)[0] as usize }
150}