Skip to main content

tightbeam/transport/handshake/
error.rs

1#[cfg(feature = "derive")]
2use crate::Errorizable;
3
4pub type Result<T> = core::result::Result<T, HandshakeError>;
5
6/// Errors specific to handshake operations
7#[cfg_attr(feature = "derive", derive(Errorizable))]
8#[derive(Debug)]
9pub enum HandshakeError {
10	// ---------------- Protocol & structure specific ----------------
11	// Invariant violations (non-panicking)
12	#[cfg_attr(
13		feature = "derive",
14		error("Handshake invariant violation: transcript already locked")
15	)]
16	TranscriptAlreadyLocked,
17	#[cfg_attr(
18		feature = "derive",
19		error("Handshake invariant violation: transcript not locked")
20	)]
21	TranscriptNotLocked,
22	#[cfg_attr(
23		feature = "derive",
24		error("Handshake invariant violation: AEAD key already derived")
25	)]
26	AeadAlreadyDerived,
27	#[cfg_attr(
28		feature = "derive",
29		error("Handshake invariant violation: Finished already sent")
30	)]
31	FinishedAlreadySent,
32	#[cfg_attr(
33		feature = "derive",
34		error("Handshake invariant violation: Finished before transcript lock")
35	)]
36	FinishedBeforeTranscriptLock,
37	/// Invalid client key exchange message
38	#[cfg_attr(feature = "derive", error("Invalid client key exchange message"))]
39	InvalidClientKeyExchange,
40
41	/// Invalid server key exchange message
42	#[cfg_attr(feature = "derive", error("Invalid server key exchange message"))]
43	InvalidServerKeyExchange,
44
45	/// Invalid public key in handshake
46	#[cfg_attr(feature = "derive", error("Invalid public key in handshake: {0}"))]
47	#[cfg_attr(feature = "derive", from)]
48	InvalidPublicKey(crate::crypto::sign::ecdsa::k256::elliptic_curve::Error),
49
50	/// Invalid certificate
51	#[cfg_attr(feature = "derive", error("Invalid certificate: {0}"))]
52	#[cfg_attr(feature = "derive", from)]
53	CertificateValidationError(crate::crypto::x509::error::CertificateValidationError),
54
55	/// Signature verification failed
56	#[cfg_attr(feature = "derive", error("Handshake signature verification failed"))]
57	SignatureVerificationFailed,
58
59	/// Signature error (parsing or verification)
60	#[cfg_attr(feature = "derive", error("Signature error: {0}"))]
61	#[cfg_attr(feature = "derive", from)]
62	SignatureError(crate::crypto::sign::Error),
63
64	/// Key derivation failed
65	#[cfg_attr(feature = "derive", error("Handshake key derivation failed: {0}"))]
66	#[cfg_attr(feature = "derive", from)]
67	KeyDerivationFailed(crate::crypto::aead::Error),
68
69	/// Underlying DER encode/decode error
70	#[cfg_attr(feature = "derive", error("DER error: {0}"))]
71	#[cfg_attr(feature = "derive", from)]
72	DerError(crate::der::Error),
73
74	/// SPKI (SubjectPublicKeyInfo) error
75	#[cfg_attr(feature = "derive", error("SPKI error: {0}"))]
76	#[cfg_attr(feature = "derive", from)]
77	SpkiError(crate::spki::Error),
78
79	/// Key provider error
80	#[cfg_attr(feature = "derive", error("Key provider error: {0}"))]
81	#[cfg_attr(feature = "derive", from)]
82	KeyError(crate::crypto::key::KeyError),
83
84	/// CMS builder error
85	#[cfg_attr(feature = "derive", error("CMS builder error: {0}"))]
86	#[cfg_attr(feature = "derive", from)]
87	CmsBuilderError(crate::cms::builder::Error),
88
89	/// Invalid handshake state
90	#[cfg_attr(feature = "derive", error("Invalid handshake state"))]
91	InvalidState,
92
93	/// Missing server key
94	#[cfg_attr(feature = "derive", error("Missing server key"))]
95	MissingServerKey,
96
97	/// Missing server certificate
98	#[cfg_attr(feature = "derive", error("Missing server certificate"))]
99	MissingServerCertificate,
100
101	/// Missing client certificate
102	#[cfg_attr(feature = "derive", error("Missing client certificate"))]
103	MissingClientCertificate,
104
105	/// Invalid transcript hash length or format
106	#[cfg_attr(feature = "derive", error("Invalid transcript hash"))]
107	InvalidTranscriptHash,
108
109	/// Server requires mutual authentication but client has no identity configured
110	#[cfg_attr(
111		feature = "derive",
112		error("Server requires mutual authentication but client has no identity")
113	)]
114	MutualAuthRequired,
115
116	/// Peer identity mismatch during re-handshake (immutable identity violation)
117	#[cfg_attr(
118		feature = "derive",
119		error("Peer identity changed during re-handshake - connection identity is immutable")
120	)]
121	PeerIdentityMismatch,
122
123	/// Missing client random
124	#[cfg_attr(feature = "derive", error("Missing client random from ClientHello"))]
125	MissingClientRandom,
126
127	/// Missing base session key
128	#[cfg_attr(feature = "derive", error("Missing base session key"))]
129	MissingBaseSessionKey,
130
131	/// Missing client random
132	#[cfg_attr(feature = "derive", error("Missing client random"))]
133	MissingClientRandomState,
134
135	/// Missing server random
136	#[cfg_attr(feature = "derive", error("Missing server random"))]
137	MissingServerRandom,
138
139	/// CMS salt (transcript hash) below minimum entropy requirement
140	#[cfg_attr(
141		feature = "derive",
142		error("CMS salt too short: {actual} bytes (minimum {minimum} required)")
143	)]
144	InsufficientSaltEntropy { actual: usize, minimum: usize },
145
146	/// Peer sent abort alert during handshake
147	#[cfg_attr(feature = "derive", error("Handshake aborted by peer: {0:?}"))]
148	AbortReceived(crate::transport::handshake::HandshakeAlert),
149
150	/// Handshake timeout
151	#[cfg_attr(feature = "derive", error("Handshake timeout"))]
152	Timeout,
153
154	/// Invalid profile selection - server selected profile not in client's offer
155	#[cfg_attr(feature = "derive", error("Server selected profile not in client's offer"))]
156	InvalidProfileSelection,
157
158	/// Negotiation error
159	#[cfg_attr(feature = "derive", error("Profile negotiation failed: {0}"))]
160	#[cfg_attr(feature = "derive", from)]
161	NegotiationError(crate::transport::handshake::negotiation::NegotiationError),
162
163	/// No mutually supported profiles found during negotiation
164	#[cfg_attr(feature = "derive", error("No mutually supported cryptographic profiles found"))]
165	NoMutualProfiles,
166
167	/// Dealer's choice failed - no supported profiles configured
168	#[cfg_attr(
169		feature = "derive",
170		error("Dealer's choice failed: no supported profiles configured")
171	)]
172	NoSupportedProfiles,
173
174	/// Profile negotiation required but no profiles configured
175	#[cfg_attr(
176		feature = "derive",
177		error("Profile negotiation required but no profiles configured on server")
178	)]
179	NegotiationRequired,
180
181	/// Certificate policy rejection
182	#[cfg_attr(feature = "derive", error("Certificate rejected by policy: {0}"))]
183	#[cfg_attr(feature = "derive", from)]
184	CertificatePolicyError(crate::crypto::policy::CryptoPolicyError),
185
186	// ---------------- Attribute / ASN.1 profile errors ----------------
187	#[cfg_attr(feature = "derive", error("Attribute must contain exactly one value"))]
188	InvalidAttributeArity,
189	#[cfg_attr(feature = "derive", error("Duplicate attribute present"))]
190	DuplicateAttribute,
191	#[cfg_attr(feature = "derive", error("Required attribute missing"))]
192	MissingAttribute,
193	#[cfg_attr(feature = "derive", error("Nonce value not valid OCTET STRING"))]
194	InvalidNonceEncoding,
195	#[cfg_attr(feature = "derive", error("Nonce length mismatch: {0}"))]
196	NonceLengthError(crate::error::ReceivedExpectedError<usize, usize>),
197	#[cfg_attr(feature = "derive", error("OCTET STRING length mismatch: {0}"))]
198	OctetStringLengthError(crate::error::ReceivedExpectedError<usize, usize>),
199	#[cfg_attr(feature = "derive", error("Version/alert value not valid INTEGER"))]
200	InvalidIntegerEncoding,
201	#[cfg_attr(feature = "derive", error("INTEGER out of range"))]
202	IntegerOutOfRange,
203	#[cfg_attr(feature = "derive", error("Unknown alert code: {0:?}"))]
204	UnknownAlertCode(u8),
205
206	// ---------------- Certificate time validation ----------------
207	#[cfg_attr(feature = "derive", error("Certificate not yet valid"))]
208	CertificateNotYetValid,
209	#[cfg_attr(feature = "derive", error("Certificate expired"))]
210	CertificateExpired,
211	#[cfg_attr(feature = "derive", error("Invalid timestamp"))]
212	InvalidTimestamp,
213
214	// ---------------- ECIES / encryption path ----------------
215	#[cfg(feature = "ecies")]
216	#[cfg_attr(feature = "derive", error("ECIES operation failed: {0}"))]
217	#[cfg_attr(feature = "derive", from)]
218	EciesError(crate::crypto::ecies::EciesError),
219	#[cfg_attr(feature = "derive", error("Missing encrypted content in ECIES message"))]
220	MissingEncryptedContent,
221	#[cfg_attr(feature = "derive", error("Invalid decrypted payload size"))]
222	InvalidDecryptedPayloadSize,
223	#[cfg_attr(feature = "derive", error("client_random mismatch - possible replay attack"))]
224	ClientRandomMismatchReplay,
225
226	// ---------------- Key agreement / CMS KARI ----------------
227	#[cfg_attr(feature = "derive", error("ECDH operation failed"))]
228	EcdhFailed,
229	#[cfg_attr(feature = "derive", error("KDF operation failed"))]
230	KdfError,
231	#[cfg_attr(
232		feature = "derive",
233		error("Invalid key size: expected {expected}, got {received}")
234	)]
235	InvalidKeySize { expected: usize, received: usize },
236	#[cfg_attr(feature = "derive", error("ASN.1 encoding error: {0}"))]
237	Asn1Error(der::Error),
238	#[cfg_attr(feature = "derive", error("Invalid recipient index"))]
239	InvalidRecipientIndex,
240	#[cfg_attr(feature = "derive", error("Missing UKM in KeyAgreeRecipientInfo"))]
241	MissingUkm,
242	#[cfg_attr(feature = "derive", error("Failed to parse originator public key"))]
243	InvalidOriginatorPublicKey,
244	#[cfg_attr(feature = "derive", error("Unsupported originator identifier type"))]
245	UnsupportedOriginatorIdentifier,
246	#[cfg_attr(feature = "derive", error("KARI builder already consumed"))]
247	KariBuilderConsumed,
248	#[cfg_attr(feature = "derive", error("Content encryption algorithm not set"))]
249	MissingContentEncryptionAlgorithm,
250	#[cfg_attr(
251		feature = "derive",
252		error("Key wrap algorithm not configured in security profile")
253	)]
254	MissingKeyWrapAlgorithm,
255	#[cfg(all(feature = "builder", feature = "aead"))]
256	#[cfg_attr(feature = "derive", error("AES key wrap operation failed: {0}"))]
257	#[cfg_attr(feature = "derive", from)]
258	AesKeyWrap(crate::crypto::aead::aes_kw::Error),
259
260	#[cfg(feature = "kem")]
261	#[cfg_attr(
262		feature = "derive",
263		error("Hybrid key agreement integrity check failed: combined ECDH+KEM key validation error")
264	)]
265	HybridKariIntegrityCheckFailed,
266
267	// ---------------- Random generation ----------------
268	#[cfg_attr(feature = "derive", error("Random generation failed"))]
269	RandomGenerationFailed,
270
271	// ---------------- Generic octet string length (server_random/client_random etc.) ----------------
272	#[cfg_attr(feature = "derive", error("Invalid OCTET STRING length: {0}"))]
273	InvalidOctetStringLength(&'static str),
274}
275
276#[cfg(not(feature = "derive"))]
277impl core::fmt::Display for HandshakeError {
278	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
279		match self {
280			HandshakeError::InvalidClientKeyExchange => write!(f, "Invalid client key exchange message"),
281			HandshakeError::InvalidServerKeyExchange => write!(f, "Invalid server key exchange message"),
282			HandshakeError::InvalidPublicKey(e) => write!(f, "Invalid public key in handshake: {}", e),
283			HandshakeError::CertificateValidationError(e) => write!(f, "Invalid certificate: {}", e),
284			HandshakeError::SpkiError(e) => write!(f, "SPKI error: {}", e),
285			HandshakeError::KeyError(e) => write!(f, "Key provider error: {}", e),
286			HandshakeError::CmsBuilderError(e) => write!(f, "CMS builder error: {}", e),
287			HandshakeError::SignatureVerificationFailed => write!(f, "Handshake signature verification failed"),
288			HandshakeError::KeyDerivationFailed(e) => write!(f, "Handshake key derivation failed: {}", e),
289			HandshakeError::InvalidState => write!(f, "Invalid handshake state"),
290			HandshakeError::MissingServerKey => write!(f, "Missing server key"),
291			HandshakeError::MissingServerCertificate => write!(f, "Missing server certificate"),
292			HandshakeError::MissingClientCertificate => write!(f, "Missing client certificate"),
293			HandshakeError::InvalidTranscriptHash => write!(f, "Invalid transcript hash"),
294			HandshakeError::MissingClientRandom => write!(f, "Missing client random from ClientHello"),
295			HandshakeError::MissingBaseSessionKey => write!(f, "Missing base session key"),
296			HandshakeError::MissingClientRandomState => write!(f, "Missing client random"),
297			HandshakeError::MissingServerRandom => write!(f, "Missing server random"),
298			HandshakeError::InsufficientSaltEntropy { actual, minimum } => {
299				write!(f, "CMS salt too short: {} bytes (minimum {} required)", actual, minimum)
300			}
301			HandshakeError::AbortReceived(alert) => write!(f, "Handshake aborted by peer: {:?}", alert),
302			HandshakeError::Timeout => write!(f, "Handshake timeout"),
303			HandshakeError::InvalidProfileSelection => write!(f, "Server selected profile not in client's offer"),
304			HandshakeError::NegotiationError(e) => write!(f, "Profile negotiation failed: {}", e),
305			HandshakeError::NoMutualProfiles => write!(f, "No mutually supported cryptographic profiles found"),
306			HandshakeError::NoSupportedProfiles => {
307				write!(f, "Dealer's choice failed: no supported profiles configured")
308			}
309			HandshakeError::NegotiationRequired => {
310				write!(f, "Profile negotiation required but no profiles configured on server")
311			}
312			HandshakeError::CertificatePolicyError(e) => write!(f, "Certificate rejected by policy: {}", e),
313			HandshakeError::DerError(e) => write!(f, "DER error: {}", e),
314			HandshakeError::InvalidAttributeArity => write!(f, "Attribute must contain exactly one value"),
315			HandshakeError::DuplicateAttribute => write!(f, "Duplicate attribute present"),
316			HandshakeError::MissingAttribute => write!(f, "Required attribute missing"),
317			HandshakeError::InvalidNonceEncoding => write!(f, "Nonce value not valid OCTET STRING"),
318			HandshakeError::NonceLengthError(e) => write!(f, "Nonce length mismatch: {}", e),
319			HandshakeError::OctetStringLengthError(e) => write!(f, "OCTET STRING length mismatch: {}", e),
320			HandshakeError::InvalidIntegerEncoding => write!(f, "Version/alert value not valid INTEGER"),
321			HandshakeError::IntegerOutOfRange => write!(f, "INTEGER out of range"),
322			HandshakeError::UnknownAlertCode(code) => write!(f, "Unknown alert code: {code}"),
323			HandshakeError::CertificateNotYetValid => write!(f, "Certificate not yet valid"),
324			HandshakeError::CertificateExpired => write!(f, "Certificate expired"),
325			HandshakeError::InvalidTimestamp => write!(f, "Invalid timestamp"),
326			HandshakeError::EciesError(e) => write!(f, "ECIES operation failed: {}", e),
327			HandshakeError::MissingEncryptedContent => write!(f, "Missing encrypted content in ECIES message"),
328			HandshakeError::InvalidDecryptedPayloadSize => write!(f, "Invalid decrypted payload size"),
329			HandshakeError::ClientRandomMismatchReplay => write!(f, "client_random mismatch - possible replay attack"),
330			HandshakeError::EcdhFailed => write!(f, "ECDH operation failed"),
331			HandshakeError::KdfError => write!(f, "KDF operation failed"),
332			HandshakeError::InvalidKeySize { expected, received } => {
333				write!(f, "Invalid key size: expected {}, got {}", expected, received)
334			}
335			HandshakeError::Asn1Error(e) => write!(f, "ASN.1 encoding error: {}", e),
336			HandshakeError::InvalidRecipientIndex => write!(f, "Invalid recipient index"),
337			HandshakeError::MissingUkm => write!(f, "Missing UKM in KeyAgreeRecipientInfo"),
338			HandshakeError::InvalidOriginatorPublicKey => write!(f, "Failed to parse originator public key"),
339			HandshakeError::UnsupportedOriginatorIdentifier => write!(f, "Unsupported originator identifier type"),
340			HandshakeError::KariBuilderConsumed => write!(f, "KARI builder already consumed"),
341			HandshakeError::MissingContentEncryptionAlgorithm => write!(f, "Content encryption algorithm not set"),
342			HandshakeError::MissingKeyWrapAlgorithm => {
343				write!(f, "Key wrap algorithm not configured in security profile")
344			}
345			#[cfg(all(feature = "builder", feature = "aead"))]
346			HandshakeError::AesKeyWrap(e) => write!(f, "AES key wrap operation failed: {}", e),
347			HandshakeError::RandomGenerationFailed => write!(f, "Random generation failed"),
348			HandshakeError::InvalidOctetStringLength(m) => write!(f, "Invalid OCTET STRING length: {}", m),
349			HandshakeError::NonceLengthError(e) => write!(f, "Nonce length mismatch: {}", e),
350			HandshakeError::OctetStringLengthError(e) => write!(f, "OCTET STRING length mismatch: {}", e),
351			HandshakeError::UnknownAlertCode(code) => write!(f, "Unknown alert code: {code}"),
352		}
353	}
354}
355
356#[cfg(not(feature = "derive"))]
357impl core::error::Error for HandshakeError {}
358
359impl From<crate::crypto::kdf::KdfError> for HandshakeError {
360	fn from(_: crate::crypto::kdf::KdfError) -> Self {
361		HandshakeError::KeyDerivationFailed(crate::crypto::aead::Error)
362	}
363}
364
365impl From<crypto_common::InvalidLength> for HandshakeError {
366	fn from(_: crypto_common::InvalidLength) -> Self {
367		HandshakeError::KeyDerivationFailed(crate::crypto::aead::Error)
368	}
369}
370
371#[cfg(not(feature = "derive"))]
372impl From<crate::crypto::key::KeyError> for HandshakeError {
373	fn from(e: crate::crypto::key::KeyError) -> Self {
374		HandshakeError::KeyError(e)
375	}
376}