Skip to main content
KeyOS API Reference

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("Duplicate shard index {index}")]
49    DuplicateShardIndex { index: usize },
50    #[error("Other error")]
51    Other,
52}
53
54#[derive(Clone, Copy, Debug, thiserror::Error, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
55pub enum KeycardIdentifyError {
56    #[error("Invalid data")]
57    InvalidData,
58    #[error("Different device ID")]
59    DifferentDeviceId,
60    #[error("Different seed fingerprint")]
61    DifferentSeedFingerprint,
62    #[error("Unauthenticated shard")]
63    HmacMismatch,
64    #[error("Existing shard")]
65    ExistingShard,
66}
67
68impl From<xous::Error> for KeycardError {
69    fn from(value: xous::Error) -> Self { KeycardError::Xous(value.to_usize()) }
70}
71
72impl From<security::AccessDenied> for KeycardError {
73    fn from(_: security::AccessDenied) -> Self { KeycardError::Security(0) }
74}
75
76impl From<security::GetDeviceIdError> for KeycardError {
77    fn from(_: security::GetDeviceIdError) -> Self { KeycardError::Security(1) }
78}
79
80impl From<crypto::error::ShamirError> for KeycardError {
81    fn from(value: crypto::error::ShamirError) -> Self { Self::Shamir(value) }
82}
83
84impl From<crypto::error::CryptoError> for KeycardError {
85    fn from(value: crypto::error::CryptoError) -> Self { Self::Crypto(value) }
86}
87
88impl From<nfc::error::NfcError> for KeycardError {
89    fn from(value: nfc::error::NfcError) -> Self { Self::Nfc(value) }
90}
91
92impl AsScalar<3> for KeycardError {
93    fn as_scalar(&self) -> [u32; 3] {
94        match self {
95            KeycardError::Xous(e) => [1, *e as u32, 0],
96            KeycardError::Security(e) => [2, *e as u32, 0],
97            KeycardError::Shamir(e) => [3, AsScalar::<1>::as_scalar(e)[0], 0],
98            KeycardError::Crypto(e) => [4, AsScalar::<1>::as_scalar(e)[0], 0],
99            KeycardError::Nfc(e) => [5, AsScalar::<1>::as_scalar(e)[0], 0],
100            KeycardError::Ndef => [6, 0, 0],
101            KeycardError::NoShardLeft => [7, 0, 0],
102            KeycardError::NotMagicBackupShard => [8, 0, 0],
103            KeycardError::InvalidData => [9, 0, 0],
104            KeycardError::DifferentDeviceId => [10, 0, 0],
105            KeycardError::DifferentSeedFingerprint => [11, 0, 0],
106            KeycardError::SeedMissing => [12, 0, 0],
107            KeycardError::NotEnoughShards => [13, 0, 0],
108            KeycardError::HmacMismatch => [14, 0, 0],
109            KeycardError::BlankShard => [15, 0, 0],
110            KeycardError::BlankTag => [16, 0, 0],
111            KeycardError::InvalidScheme => [17, 0, 0],
112            KeycardError::SchemeMismatch => [18, 0, 0],
113            KeycardError::TimestampMismatch { expected, found } => [19, *expected, *found],
114            KeycardError::DuplicateShardIndex { index } => [20, *index as u32, 0],
115            KeycardError::Other => [0, 0, 0],
116        }
117    }
118}
119
120impl FromScalar<3> for KeycardError {
121    fn from_scalar(value: [u32; 3]) -> Self {
122        match value[0] {
123            1 => KeycardError::Xous(value[1] as usize),
124            2 => KeycardError::Security(value[1] as usize),
125            3 => KeycardError::Shamir(crypto::error::ShamirError::from_scalar([value[1]])),
126            4 => KeycardError::Crypto(crypto::error::CryptoError::from_scalar([value[1]])),
127            5 => KeycardError::Nfc(nfc::error::NfcError::from_scalar([value[1]])),
128            6 => KeycardError::Ndef,
129            7 => KeycardError::NoShardLeft,
130            8 => KeycardError::NotMagicBackupShard,
131            9 => KeycardError::InvalidData,
132            10 => KeycardError::DifferentDeviceId,
133            11 => KeycardError::DifferentSeedFingerprint,
134            12 => KeycardError::SeedMissing,
135            13 => KeycardError::NotEnoughShards,
136            14 => KeycardError::HmacMismatch,
137            15 => KeycardError::BlankShard,
138            16 => KeycardError::BlankTag,
139            17 => KeycardError::InvalidScheme,
140            18 => KeycardError::SchemeMismatch,
141            19 => KeycardError::TimestampMismatch { expected: value[1], found: value[2] },
142            20 => KeycardError::DuplicateShardIndex { index: value[1] as usize },
143            _ => KeycardError::Other,
144        }
145    }
146}
147
148impl From<usize> for KeycardError {
149    fn from(value: usize) -> Self { Self::from_scalar([value as u32, 0, 0]) }
150}
151
152impl From<KeycardError> for usize {
153    fn from(value: KeycardError) -> Self { server::AsScalar::<3>::as_scalar(&value)[0] as usize }
154}