1#[cfg(not(feature = "std"))]
2extern crate alloc;
3#[cfg(not(feature = "std"))]
4use alloc::string::ToString;
5
6#[cfg(feature = "derive")]
7use crate::Errorizable;
8
9pub type Result<T> = core::result::Result<T, HandshakeError>;
10
11#[cfg_attr(feature = "derive", derive(Errorizable))]
13#[derive(Debug)]
14pub enum HandshakeError {
15 #[cfg_attr(
18 feature = "derive",
19 error("Handshake invariant violation: transcript already locked")
20 )]
21 TranscriptAlreadyLocked,
22 #[cfg_attr(
23 feature = "derive",
24 error("Handshake invariant violation: transcript not locked")
25 )]
26 TranscriptNotLocked,
27 #[cfg_attr(
28 feature = "derive",
29 error("Handshake invariant violation: AEAD key already derived")
30 )]
31 AeadAlreadyDerived,
32 #[cfg_attr(
33 feature = "derive",
34 error("Handshake invariant violation: Finished already sent")
35 )]
36 FinishedAlreadySent,
37 #[cfg_attr(
38 feature = "derive",
39 error("Handshake invariant violation: Finished before transcript lock")
40 )]
41 FinishedBeforeTranscriptLock,
42 #[cfg_attr(feature = "derive", error("Invalid client key exchange message"))]
44 InvalidClientKeyExchange,
45
46 #[cfg_attr(feature = "derive", error("Invalid server key exchange message"))]
48 InvalidServerKeyExchange,
49
50 #[cfg_attr(feature = "derive", error("Invalid public key in handshake: {0}"))]
52 #[cfg_attr(feature = "derive", from)]
53 InvalidPublicKey(crate::crypto::sign::ecdsa::k256::elliptic_curve::Error),
54
55 #[cfg_attr(feature = "derive", error("Invalid certificate: {0}"))]
57 #[cfg_attr(feature = "derive", from)]
58 CertificateValidationError(crate::crypto::x509::error::CertificateValidationError),
59
60 #[cfg_attr(feature = "derive", error("Handshake signature verification failed"))]
62 SignatureVerificationFailed,
63
64 #[cfg_attr(feature = "derive", error("Signature error: {0}"))]
66 #[cfg_attr(feature = "derive", from)]
67 SignatureError(crate::crypto::sign::Error),
68
69 #[cfg_attr(feature = "derive", error("Handshake key derivation failed: {0}"))]
71 #[cfg_attr(feature = "derive", from)]
72 KeyDerivationFailed(crate::crypto::aead::Error),
73
74 #[cfg_attr(feature = "derive", error("DER error: {0}"))]
76 #[cfg_attr(feature = "derive", from)]
77 DerError(crate::der::Error),
78
79 #[cfg_attr(feature = "derive", error("SPKI error: {0}"))]
81 #[cfg_attr(feature = "derive", from)]
82 SpkiError(crate::spki::Error),
83
84 #[cfg_attr(feature = "derive", error("Key provider error: {0}"))]
86 #[cfg_attr(feature = "derive", from)]
87 KeyError(crate::crypto::key::KeyError),
88
89 #[cfg_attr(feature = "derive", error("CMS builder error: {0}"))]
91 #[cfg_attr(feature = "derive", from)]
92 CmsBuilderError(crate::cms::builder::Error),
93
94 #[cfg_attr(feature = "derive", error("Invalid handshake state"))]
96 InvalidState,
97
98 #[cfg_attr(feature = "derive", error("Missing server key"))]
100 MissingServerKey,
101
102 #[cfg_attr(
104 feature = "derive",
105 error("Trust store required: refusing expiry-only peer certificate validation")
106 )]
107 MissingTrustStore,
108
109 #[cfg_attr(feature = "derive", error("Missing server certificate"))]
111 MissingServerCertificate,
112
113 #[cfg_attr(feature = "derive", error("Missing client certificate"))]
115 MissingClientCertificate,
116
117 #[cfg_attr(feature = "derive", error("Invalid transcript hash"))]
119 InvalidTranscriptHash,
120
121 #[cfg_attr(
123 feature = "derive",
124 error("Transcript digest length invalid: expected {expected} bytes, got {received}")
125 )]
126 TranscriptDigestLength { expected: usize, received: usize },
127
128 #[cfg_attr(
130 feature = "derive",
131 error("Server requires mutual authentication but client has no identity")
132 )]
133 MutualAuthRequired,
134
135 #[cfg_attr(
137 feature = "derive",
138 error("Peer identity changed during re-handshake - connection identity is immutable")
139 )]
140 PeerIdentityMismatch,
141
142 #[cfg_attr(
144 feature = "derive",
145 error("Provisioned certificate chain leaf does not match pinned server certificate")
146 )]
147 PinnedCertificateMismatch,
148
149 #[cfg_attr(feature = "derive", error("Missing client random from ClientHello"))]
151 MissingClientRandom,
152
153 #[cfg_attr(feature = "derive", error("Missing base session key"))]
155 MissingBaseSessionKey,
156
157 #[cfg_attr(feature = "derive", error("Missing client random"))]
159 MissingClientRandomState,
160
161 #[cfg_attr(feature = "derive", error("Missing server random"))]
163 MissingServerRandom,
164
165 #[cfg_attr(
167 feature = "derive",
168 error("CMS salt too short: {actual} bytes (minimum {minimum} required)")
169 )]
170 InsufficientSaltEntropy { actual: usize, minimum: usize },
171
172 #[cfg_attr(feature = "derive", error("Handshake aborted by peer: {0:?}"))]
174 AbortReceived(crate::transport::handshake::HandshakeAlert),
175
176 #[cfg_attr(feature = "derive", error("Handshake timeout"))]
178 Timeout,
179
180 #[cfg_attr(feature = "derive", error("Server selected profile not in client's offer"))]
182 InvalidProfileSelection,
183
184 #[cfg_attr(feature = "derive", error("Profile negotiation failed: {0}"))]
186 #[cfg_attr(feature = "derive", from)]
187 NegotiationError(crate::transport::handshake::negotiation::NegotiationError),
188
189 #[cfg_attr(feature = "derive", error("No mutually supported cryptographic profiles found"))]
191 NoMutualProfiles,
192
193 #[cfg_attr(
195 feature = "derive",
196 error("Dealer's choice failed: no supported profiles configured")
197 )]
198 NoSupportedProfiles,
199
200 #[cfg_attr(
202 feature = "derive",
203 error("Profile negotiation required but no profiles configured on server")
204 )]
205 NegotiationRequired,
206
207 #[cfg_attr(feature = "derive", error("Certificate rejected by policy: {0}"))]
209 #[cfg_attr(feature = "derive", from)]
210 CertificatePolicyError(crate::crypto::policy::CryptoPolicyError),
211
212 #[cfg_attr(feature = "derive", error("Attribute must contain exactly one value"))]
214 InvalidAttributeArity,
215 #[cfg_attr(feature = "derive", error("Duplicate attribute present"))]
216 DuplicateAttribute,
217 #[cfg_attr(feature = "derive", error("Required attribute missing"))]
218 MissingAttribute,
219 #[cfg_attr(
220 feature = "derive",
221 error("Too many supported curves: {count} exceeds cap of {max}")
222 )]
223 TooManySupportedCurves { count: usize, max: usize },
224 #[cfg_attr(feature = "derive", error("Nonce value not valid OCTET STRING"))]
225 InvalidNonceEncoding,
226 #[cfg_attr(feature = "derive", error("Nonce length mismatch: {0}"))]
227 NonceLengthError(crate::error::ReceivedExpectedError<usize, usize>),
228 #[cfg_attr(feature = "derive", error("OCTET STRING length mismatch: {0}"))]
229 OctetStringLengthError(crate::error::ReceivedExpectedError<usize, usize>),
230 #[cfg_attr(feature = "derive", error("Version/alert value not valid INTEGER"))]
231 InvalidIntegerEncoding,
232 #[cfg_attr(feature = "derive", error("INTEGER out of range"))]
233 IntegerOutOfRange,
234 #[cfg_attr(feature = "derive", error("Unknown alert code: {0:?}"))]
235 UnknownAlertCode(u8),
236
237 #[cfg_attr(feature = "derive", error("Certificate not yet valid"))]
239 CertificateNotYetValid,
240 #[cfg_attr(feature = "derive", error("Certificate expired"))]
241 CertificateExpired,
242 #[cfg_attr(feature = "derive", error("Invalid timestamp"))]
243 InvalidTimestamp,
244
245 #[cfg(feature = "ecies")]
247 #[cfg_attr(feature = "derive", error("ECIES operation failed: {0}"))]
248 #[cfg_attr(feature = "derive", from)]
249 EciesError(crate::crypto::ecies::EciesError),
250 #[cfg_attr(feature = "derive", error("Missing encrypted content in ECIES message"))]
251 MissingEncryptedContent,
252 #[cfg_attr(feature = "derive", error("Invalid decrypted payload size"))]
253 InvalidDecryptedPayloadSize,
254 #[cfg_attr(feature = "derive", error("client_random mismatch - possible replay attack"))]
255 ClientRandomMismatchReplay,
256
257 #[cfg_attr(feature = "derive", error("ECDH operation failed"))]
259 EcdhFailed,
260 #[cfg_attr(feature = "derive", error("KDF operation failed"))]
261 KdfError,
262 #[cfg_attr(
263 feature = "derive",
264 error("Invalid key size: expected {expected}, got {received}")
265 )]
266 InvalidKeySize { expected: usize, received: usize },
267 #[cfg_attr(
268 feature = "derive",
269 error("Ciphertext too short: {received} bytes (minimum {minimum} required)")
270 )]
271 CiphertextTooShort { minimum: usize, received: usize },
272 #[cfg_attr(feature = "derive", error("ASN.1 encoding error: {0}"))]
273 Asn1Error(der::Error),
274 #[cfg_attr(feature = "derive", error("Invalid recipient index"))]
275 InvalidRecipientIndex,
276 #[cfg_attr(feature = "derive", error("Missing UKM in KeyAgreeRecipientInfo"))]
277 MissingUkm,
278 #[cfg_attr(feature = "derive", error("Failed to parse originator public key"))]
279 InvalidOriginatorPublicKey,
280 #[cfg_attr(feature = "derive", error("Unsupported originator identifier type"))]
281 UnsupportedOriginatorIdentifier,
282 #[cfg_attr(feature = "derive", error("KARI builder already consumed"))]
283 KariBuilderConsumed,
284 #[cfg_attr(feature = "derive", error("Content encryption algorithm not set"))]
285 MissingContentEncryptionAlgorithm,
286 #[cfg_attr(
287 feature = "derive",
288 error("Key wrap algorithm not configured in security profile")
289 )]
290 MissingKeyWrapAlgorithm,
291 #[cfg_attr(
292 feature = "derive",
293 error("Negotiated key wrap algorithm unsupported (expected AES-128/192/256 key wrap)")
294 )]
295 UnsupportedKeyWrapAlgorithm,
296 #[cfg_attr(
297 feature = "derive",
298 error("Negotiated AEAD algorithm unsupported (expected AES-128/256 GCM)")
299 )]
300 UnsupportedAeadAlgorithm,
301 #[cfg_attr(
302 feature = "derive",
303 error("Peer aead_key_size {declared} does not match negotiated AEAD key size {expected}")
304 )]
305 AeadKeySizeMismatch { declared: usize, expected: usize },
306 #[cfg(all(feature = "builder", feature = "aead"))]
307 #[cfg_attr(feature = "derive", error("AES key wrap operation failed: {0}"))]
308 #[cfg_attr(feature = "derive", from)]
309 AesKeyWrap(crate::crypto::aead::aes_kw::Error),
310
311 #[cfg(feature = "kem")]
312 #[cfg_attr(
313 feature = "derive",
314 error("Hybrid key agreement integrity check failed: combined ECDH+KEM key validation error")
315 )]
316 HybridKariIntegrityCheckFailed,
317
318 #[cfg_attr(feature = "derive", error("Random generation failed"))]
320 RandomGenerationFailed,
321
322 #[cfg_attr(feature = "derive", error("Secret unavailable: {0}"))]
324 #[cfg_attr(feature = "derive", from)]
325 SecretUnavailable(crate::crypto::secret::SecretError),
326
327 #[cfg_attr(feature = "derive", error("Invalid OCTET STRING length: {0}"))]
329 InvalidOctetStringLength(&'static str),
330}
331
332crate::impl_error_display!(HandshakeError {
334 TranscriptAlreadyLocked => "Handshake invariant violation: transcript already locked",
335 TranscriptNotLocked => "Handshake invariant violation: transcript not locked",
336 AeadAlreadyDerived => "Handshake invariant violation: AEAD key already derived",
337 FinishedAlreadySent => "Handshake invariant violation: Finished already sent",
338 FinishedBeforeTranscriptLock => "Handshake invariant violation: Finished before transcript lock",
339 InvalidClientKeyExchange => "Invalid client key exchange message",
340 InvalidServerKeyExchange => "Invalid server key exchange message",
341 InvalidPublicKey(e) => "Invalid public key in handshake: {e}",
342 CertificateValidationError(e) => "Invalid certificate: {e}",
343 SignatureVerificationFailed => "Handshake signature verification failed",
344 SignatureError(e) => "Signature error: {e}",
345 KeyDerivationFailed(e) => "Handshake key derivation failed: {e}",
346 DerError(e) => "DER error: {e}",
347 SpkiError(e) => "SPKI error: {e}",
348 KeyError(e) => "Key provider error: {e}",
349 CmsBuilderError(e) => "CMS builder error: {e}",
350 InvalidState => "Invalid handshake state",
351 MissingServerKey => "Missing server key",
352 MissingTrustStore => "Trust store required: refusing expiry-only peer certificate validation",
353 MissingServerCertificate => "Missing server certificate",
354 MissingClientCertificate => "Missing client certificate",
355 InvalidTranscriptHash => "Invalid transcript hash",
356 TranscriptDigestLength { expected, received } => "Transcript digest length invalid: expected {expected} bytes, got {received}",
357 MutualAuthRequired => "Server requires mutual authentication but client has no identity",
358 PeerIdentityMismatch => "Peer identity changed during re-handshake - connection identity is immutable",
359 PinnedCertificateMismatch => "Provisioned certificate chain leaf does not match pinned server certificate",
360 MissingClientRandom => "Missing client random from ClientHello",
361 MissingBaseSessionKey => "Missing base session key",
362 MissingClientRandomState => "Missing client random",
363 MissingServerRandom => "Missing server random",
364 InsufficientSaltEntropy { actual, minimum } => "CMS salt too short: {actual} bytes (minimum {minimum} required)",
365 AbortReceived(alert) => "Handshake aborted by peer: {alert:?}",
366 Timeout => "Handshake timeout",
367 InvalidProfileSelection => "Server selected profile not in client's offer",
368 NegotiationError(e) => "Profile negotiation failed: {e}",
369 NoMutualProfiles => "No mutually supported cryptographic profiles found",
370 NoSupportedProfiles => "Dealer's choice failed: no supported profiles configured",
371 NegotiationRequired => "Profile negotiation required but no profiles configured on server",
372 CertificatePolicyError(e) => "Certificate rejected by policy: {e}",
373 InvalidAttributeArity => "Attribute must contain exactly one value",
374 DuplicateAttribute => "Duplicate attribute present",
375 MissingAttribute => "Required attribute missing",
376 TooManySupportedCurves { count, max } => "Too many supported curves: {count} exceeds cap of {max}",
377 InvalidNonceEncoding => "Nonce value not valid OCTET STRING",
378 NonceLengthError(e) => "Nonce length mismatch: {e}",
379 OctetStringLengthError(e) => "OCTET STRING length mismatch: {e}",
380 InvalidIntegerEncoding => "Version/alert value not valid INTEGER",
381 IntegerOutOfRange => "INTEGER out of range",
382 UnknownAlertCode(code) => "Unknown alert code: {code:?}",
383 CertificateNotYetValid => "Certificate not yet valid",
384 CertificateExpired => "Certificate expired",
385 InvalidTimestamp => "Invalid timestamp",
386 MissingEncryptedContent => "Missing encrypted content in ECIES message",
387 InvalidDecryptedPayloadSize => "Invalid decrypted payload size",
388 ClientRandomMismatchReplay => "client_random mismatch - possible replay attack",
389 EcdhFailed => "ECDH operation failed",
390 KdfError => "KDF operation failed",
391 InvalidKeySize { expected, received } => "Invalid key size: expected {expected}, got {received}",
392 CiphertextTooShort { minimum, received } => "Ciphertext too short: {received} bytes (minimum {minimum} required)",
393 Asn1Error(e) => "ASN.1 encoding error: {e}",
394 InvalidRecipientIndex => "Invalid recipient index",
395 MissingUkm => "Missing UKM in KeyAgreeRecipientInfo",
396 InvalidOriginatorPublicKey => "Failed to parse originator public key",
397 UnsupportedOriginatorIdentifier => "Unsupported originator identifier type",
398 KariBuilderConsumed => "KARI builder already consumed",
399 MissingContentEncryptionAlgorithm => "Content encryption algorithm not set",
400 MissingKeyWrapAlgorithm => "Key wrap algorithm not configured in security profile",
401 UnsupportedKeyWrapAlgorithm => "Negotiated key wrap algorithm unsupported (expected AES-128/192/256 key wrap)",
402 UnsupportedAeadAlgorithm => "Negotiated AEAD algorithm unsupported (expected AES-128/256 GCM)",
403 AeadKeySizeMismatch { declared, expected } => "Peer aead_key_size {declared} does not match negotiated AEAD key size {expected}",
404 RandomGenerationFailed => "Random generation failed",
405 SecretUnavailable(e) => "Secret unavailable: {e}",
406 InvalidOctetStringLength(m) => "Invalid OCTET STRING length: {m}",
407
408 #[cfg(feature = "ecies")]
409 EciesError(e) => "ECIES operation failed: {e}",
410 #[cfg(all(feature = "builder", feature = "aead"))]
411 AesKeyWrap(e) => "AES key wrap operation failed: {e}",
412 #[cfg(feature = "kem")]
413 HybridKariIntegrityCheckFailed => "Hybrid key agreement integrity check failed: combined ECDH+KEM key validation error",
414});
415
416impl From<crate::crypto::kdf::KdfError> for HandshakeError {
417 fn from(_: crate::crypto::kdf::KdfError) -> Self {
418 HandshakeError::KeyDerivationFailed(crate::crypto::aead::Error)
419 }
420}
421
422impl From<crate::error::TightBeamError> for HandshakeError {
425 fn from(err: crate::error::TightBeamError) -> Self {
426 use crate::error::TightBeamError;
427 match err {
428 TightBeamError::HandshakeError(h) => h,
429 TightBeamError::SerializationError(e) => HandshakeError::DerError(e),
430 #[cfg(feature = "x509")]
431 TightBeamError::SpkiError(e) => HandshakeError::SpkiError(e),
432 #[cfg(feature = "x509")]
433 TightBeamError::CertificateValidationError(e) => HandshakeError::CertificateValidationError(e),
434 #[cfg(feature = "crypto")]
435 TightBeamError::CryptoPolicyError(e) => HandshakeError::CertificatePolicyError(e),
436 #[cfg(feature = "crypto")]
437 TightBeamError::KeyError(e) => HandshakeError::KeyError(e),
438 #[cfg(feature = "signature")]
439 TightBeamError::SignatureError(e) => HandshakeError::SignatureError(e),
440 #[cfg(feature = "ecies")]
441 TightBeamError::EciesError(e) => HandshakeError::EciesError(e),
442 #[cfg(feature = "crypto")]
443 TightBeamError::SecretUnavailable(e) => HandshakeError::SecretUnavailable(e),
444 #[cfg(feature = "random")]
445 TightBeamError::OsRngError(_) => HandshakeError::RandomGenerationFailed,
446 _ => HandshakeError::InvalidState,
447 }
448 }
449}
450
451#[cfg(all(feature = "crypto", not(feature = "derive")))]
452impl From<crate::crypto::secret::SecretError> for HandshakeError {
453 fn from(err: crate::crypto::secret::SecretError) -> Self {
454 HandshakeError::SecretUnavailable(err)
455 }
456}
457
458#[cfg(all(feature = "builder", feature = "aead"))]
462impl From<HandshakeError> for crate::cms::builder::Error {
463 fn from(err: HandshakeError) -> Self {
464 match err {
465 HandshakeError::CmsBuilderError(e) => e,
466 HandshakeError::DerError(e) => crate::cms::builder::Error::Asn1(e),
467 HandshakeError::Asn1Error(e) => crate::cms::builder::Error::Asn1(e),
468 HandshakeError::SpkiError(e) => crate::cms::builder::Error::PublicKey(e),
469 other => crate::cms::builder::Error::Builder(other.to_string()),
470 }
471 }
472}
473
474impl From<crypto_common::InvalidLength> for HandshakeError {
475 fn from(_: crypto_common::InvalidLength) -> Self {
476 HandshakeError::KeyDerivationFailed(crate::crypto::aead::Error)
477 }
478}
479
480#[cfg(not(feature = "derive"))]
481impl From<crate::crypto::key::KeyError> for HandshakeError {
482 fn from(e: crate::crypto::key::KeyError) -> Self {
483 HandshakeError::KeyError(e)
484 }
485}