kipuka_coap/lib.rs
1//! EST over CoAP (RFC 9148) transport for constrained devices.
2//!
3//! This crate implements the CoAP transport binding for Enrollment over Secure
4//! Transport, enabling EST operations on constrained IoT devices that cannot
5//! use HTTP/TLS.
6//!
7//! # Protocol Mapping
8//!
9//! RFC 9148 maps EST operations to CoAP as follows:
10//! - HTTPS transport is replaced by CoAP over DTLS ("coaps")
11//! - EST URI paths use abbreviated names (e.g., `/sen` for `/simpleenroll`)
12//! - HTTP Content-Type headers map to CoAP Content-Format option IDs
13//! - Large payloads (PQC certificates can exceed 7KB) use RFC 7959 block-wise transfer
14//!
15//! # Modules
16//!
17//! - [`server`]: CoAP message parsing, encoding, and EST-coaps URI routing
18//! - [`dtls`]: DTLS session management abstraction for CoAP security
19//! - [`block`]: RFC 7959 block-wise transfer for large EST payloads
20//! - [`content_format`]: CoAP content-format IDs for EST media types (RFC 9148 §5.4)
21
22pub mod block;
23pub mod content_format;
24pub mod dtls;
25pub mod server;
26
27pub use server::{AuditInfo, CoapDtlsServer, EstHandler, EstResponse};
28
29use thiserror::Error;
30
31/// Errors arising from CoAP/EST-coaps protocol handling.
32#[derive(Debug, Error, Clone)]
33pub enum CoapError {
34 /// Malformed CoAP message (header, token, or option encoding).
35 #[error("Invalid CoAP message: {0}")]
36 InvalidMessage(String),
37
38 /// Unrecognized or unsupported CoAP method code.
39 #[error("Unsupported CoAP method: {0}")]
40 UnsupportedMethod(String),
41
42 /// Unrecognized CoAP Content-Format option value.
43 ///
44 /// RFC 9148 §5.4 defines the content-format IDs that EST-coaps supports.
45 #[error("Unsupported Content-Format: {0}")]
46 UnsupportedContentFormat(u16),
47
48 /// Block-wise transfer failure per RFC 7959.
49 #[error("Block transfer error: {0}")]
50 BlockTransferError(String),
51
52 /// DTLS session establishment or resumption failure.
53 ///
54 /// RFC 9148 §5 requires DTLS to secure all EST-coaps exchanges.
55 #[error("DTLS error: {0}")]
56 DtlsError(String),
57
58 /// Payload exceeds the configured maximum size.
59 ///
60 /// Even with block-wise transfer, reassembled payloads are bounded to
61 /// prevent resource exhaustion on constrained devices.
62 #[error("Payload too large: {size} bytes exceeds maximum {max} bytes")]
63 PayloadTooLarge {
64 /// Actual payload size in bytes.
65 size: usize,
66 /// Configured maximum payload size in bytes.
67 max: usize,
68 },
69
70 /// No CoAP resource matches the requested URI path.
71 #[error("Resource not found: {0}")]
72 ResourceNotFound(String),
73
74 /// Client authentication required but not provided (4.01).
75 ///
76 /// Returned when an EST operation requires mTLS client certificate
77 /// authentication but no certificate was presented during the DTLS
78 /// handshake.
79 #[error("Unauthorized: {0}")]
80 Unauthorized(String),
81
82 /// Authenticated client is not authorized for the request (4.03).
83 ///
84 /// The peer authenticated successfully but the request is denied by an
85 /// access-control policy — e.g. the EST enrollment-authorization check
86 /// (NIAP CA PP FDP_ACF.1): the CSR names an identity the requester is not
87 /// permitted to enroll. Distinct from [`Unauthorized`](Self::Unauthorized),
88 /// which means authentication itself is missing.
89 #[error("Forbidden: {0}")]
90 Forbidden(String),
91
92 /// Internal server error (catch-all).
93 #[error("Internal error: {0}")]
94 Internal(String),
95}
96
97/// Result type for CoAP operations.
98pub type CoapResult<T> = Result<T, CoapError>;