1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! Crate-wide error type. `core::error::Error`-compatible.
use core::fmt;
/// Convenience [`Result`] alias.
pub type Result<T> = core::result::Result<T, Error>;
/// Crate-wide error.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// Buffer too small to hold a syntactically valid packet header.
Truncated {
/// Bytes available.
have: usize,
/// Bytes required.
need: usize,
},
/// SOM mismatch — packet did not start with `0x53`.
BadSom(u8),
/// `LEN` field disagrees with the actual buffer length.
BadLength {
/// Length declared in the LEN field.
declared: usize,
/// Length actually delivered.
actual: usize,
},
/// Reserved bits in the [`crate::ControlByte`] are non-zero, or the byte is
/// otherwise malformed.
BadControlByte(u8),
/// CRC verification failed.
BadCrc {
/// CRC carried on the wire.
got: u16,
/// CRC we computed from the header + payload.
want: u16,
},
/// Checksum verification failed.
BadChecksum {
/// Trailer byte carried on the wire.
got: u8,
/// Checksum we computed.
want: u8,
},
/// Address field is reserved or invalid.
BadAddress(u8),
/// Sequence number is not in `0..=3`.
BadSqn(u8),
/// Security block type is unknown.
BadSecurityBlock(u8),
/// Length of the security block is wrong for its type.
BadSecurityBlockLength(u8),
/// MAC verification failed (constant-time compare).
BadMac,
/// MAC is too short (must be at least 4 bytes on the wire).
ShortMac,
/// Command code is not recognized.
UnknownCommand(u8),
/// Reply code is not recognized.
UnknownReply(u8),
/// Payload bytes do not match the expected layout for this command/reply.
MalformedPayload {
/// Command or reply code.
code: u8,
/// One-line explanation.
reason: &'static str,
},
/// Multi-part receiver detected an invariant violation.
Multipart(MultipartError),
/// Secure-channel state machine rejected an event.
SecureSession(SecureSessionError),
/// Transport-level I/O.
Io(&'static str),
/// Reply did not arrive within the configured budget.
Timeout,
/// PD declared off-line.
Offline,
/// Address mismatch between expected PD and reply ADDR.
AddrMismatch {
/// Address we sent to.
sent: u8,
/// Address echoed back in the reply.
got: u8,
},
/// PD answered with [`crate::reply::Nak`].
Nak {
/// Error code byte from the reply.
code: u8,
},
/// Output buffer was too small.
BufferOverflow {
/// Minimum required capacity.
need: usize,
/// Capacity available.
have: usize,
},
}
/// Subcategory of multi-part assembler errors.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum MultipartError {
/// First fragment did not arrive at offset 0.
UnexpectedFirstOffset(u16),
/// Out-of-order or overlapping fragment.
OutOfOrderOffset {
/// Expected next offset.
expected: u16,
/// Offset declared by the fragment we received.
got: u16,
},
/// `MpSizeTotal` differs from the figure in a previous fragment.
InconsistentTotal {
/// First fragment's stated total.
first: u16,
/// New fragment's stated total.
now: u16,
},
/// Final fragment overruns the declared total.
OverflowsTotal,
/// `MpFragmentSize` does not match the actual payload.
BadFragmentSize,
/// Counterparty aborted the transfer.
Aborted,
}
/// Subcategory of secure-channel state errors.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SecureSessionError {
/// Cryptogram comparison failed.
BadCryptogram,
/// Caller attempted to wrap a frame before SCS was fully established.
NotSecure,
/// SCS event arrived in a state that does not accept it.
BadTransition,
/// Encrypted DATA must be padded to a 16-byte multiple.
BadPadding,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Truncated { have, need } => {
write!(f, "truncated: have {have} bytes, need {need}")
}
Error::BadSom(b) => write!(f, "bad SOM byte: {b:#04x}"),
Error::BadLength { declared, actual } => {
write!(f, "length mismatch: declared {declared}, actual {actual}")
}
Error::BadControlByte(b) => write!(f, "bad control byte: {b:#04x}"),
Error::BadCrc { got, want } => write!(f, "bad CRC: got {got:#06x}, want {want:#06x}"),
Error::BadChecksum { got, want } => {
write!(f, "bad checksum: got {got:#04x}, want {want:#04x}")
}
Error::BadAddress(b) => write!(f, "bad address: {b:#04x}"),
Error::BadSqn(b) => write!(f, "bad sequence number: {b}"),
Error::BadSecurityBlock(b) => write!(f, "unknown SCB type: {b:#04x}"),
Error::BadSecurityBlockLength(b) => write!(f, "bad SCB length for type {b:#04x}"),
Error::BadMac => f.write_str("bad MAC"),
Error::ShortMac => f.write_str("MAC too short"),
Error::UnknownCommand(c) => write!(f, "unknown command code: {c:#04x}"),
Error::UnknownReply(c) => write!(f, "unknown reply code: {c:#04x}"),
Error::MalformedPayload { code, reason } => {
write!(f, "malformed payload for {code:#04x}: {reason}")
}
Error::Multipart(e) => write!(f, "multipart: {e:?}"),
Error::SecureSession(e) => write!(f, "secure session: {e:?}"),
Error::Io(s) => write!(f, "io: {s}"),
Error::Timeout => f.write_str("reply timed out"),
Error::Offline => f.write_str("PD declared off-line"),
Error::AddrMismatch { sent, got } => {
write!(f, "address mismatch: sent {sent:#04x}, got {got:#04x}")
}
Error::Nak { code } => write!(f, "PD replied NAK ({code:#04x})"),
Error::BufferOverflow { need, have } => {
write!(f, "buffer overflow: need {need}, have {have}")
}
}
}
}
impl core::error::Error for Error {}
impl From<MultipartError> for Error {
fn from(e: MultipartError) -> Self {
Error::Multipart(e)
}
}
impl From<SecureSessionError> for Error {
fn from(e: SecureSessionError) -> Self {
Error::SecureSession(e)
}
}