0.00% Lines (0/4) 0.00% Functions (0/2)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // Copyright (c) 2026 Steve Gerbino 4   // Copyright (c) 2026 Steve Gerbino
5   // 5   //
6   // Distributed under the Boost Software License, Version 1.0. (See accompanying 6   // Distributed under the Boost Software License, Version 1.0. (See accompanying
7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8   // 8   //
9   // Official repository: https://github.com/cppalliance/corosio 9   // Official repository: https://github.com/cppalliance/corosio
10   // 10   //
11   11  
12   #ifndef BOOST_COROSIO_TLS_STREAM_HPP 12   #ifndef BOOST_COROSIO_TLS_STREAM_HPP
13   #define BOOST_COROSIO_TLS_STREAM_HPP 13   #define BOOST_COROSIO_TLS_STREAM_HPP
14   14  
15   #include <boost/corosio/detail/config.hpp> 15   #include <boost/corosio/detail/config.hpp>
16   #include <boost/capy/buffers.hpp> 16   #include <boost/capy/buffers.hpp>
17   #include <boost/capy/detail/buffer_array.hpp> 17   #include <boost/capy/detail/buffer_array.hpp>
18   #include <boost/capy/io/any_stream.hpp> 18   #include <boost/capy/io/any_stream.hpp>
19   #include <boost/capy/io_task.hpp> 19   #include <boost/capy/io_task.hpp>
20   20  
21   #include <cstddef> 21   #include <cstddef>
22   #include <string_view> 22   #include <string_view>
23   23  
24   namespace boost::corosio { 24   namespace boost::corosio {
25   25  
26   /** TLS handshake role. 26   /** TLS handshake role.
27   27  
28   Specifies whether to perform the TLS handshake as a client or server. 28   Specifies whether to perform the TLS handshake as a client or server.
29   29  
30   @see tls_stream::handshake 30   @see tls_stream::handshake
31   */ 31   */
32   enum class tls_role 32   enum class tls_role
33   { 33   {
34   /// Perform handshake as the connecting client. 34   /// Perform handshake as the connecting client.
35   client, 35   client,
36   36  
37   /// Perform handshake as the accepting server. 37   /// Perform handshake as the accepting server.
38   server 38   server
39   }; 39   };
40   40  
41 - /** Reads, writes, and manages the handshake lifecycle of a TLS 41 + /** Abstract base class for TLS streams.
42 - session over an underlying stream.  
43   42  
44   This class provides a runtime-polymorphic interface for TLS 43   This class provides a runtime-polymorphic interface for TLS
45 - implementations. Derived classes (`openssl_stream`, `wolfssl_stream`) 44 + implementations. Derived classes (openssl_stream, wolfssl_stream)
46   implement the virtual functions to provide backend-specific 45   implement the virtual functions to provide backend-specific
47   TLS functionality. 46   TLS functionality.
48   47  
49 - An @ref io_stream represents OS-level I/O completed by the kernel. 48 + Unlike @ref io_stream which represents OS-level I/O completed
50 - TLS streams are coroutine-based instead: their operations are 49 + by the kernel, TLS streams are coroutine-based: their operations
51 - coroutines that orchestrate sub-operations on the underlying stream. 50 + are implemented as coroutines that orchestrate sub-operations
  51 + on the underlying stream.
52   52  
53   The non-virtual template wrappers (`read_some`, `write_some`) 53   The non-virtual template wrappers (`read_some`, `write_some`)
54   satisfy the `capy::Stream` concept, enabling TLS streams to 54   satisfy the `capy::Stream` concept, enabling TLS streams to
55   be used anywhere a Stream is expected. 55   be used anywhere a Stream is expected.
56   56  
57   @par Thread Safety 57   @par Thread Safety
58   Distinct objects: Safe.@n 58   Distinct objects: Safe.@n
59   Shared objects: Unsafe, with one exception: one read operation and 59   Shared objects: Unsafe, with one exception: one read operation and
60   one write operation may be in flight simultaneously. `shutdown()` 60   one write operation may be in flight simultaneously. `shutdown()`
61 - may overlap a pending read. On a multi-threaded execution context, 61 + may overlap a pending read. When the execution context runs on
62 - all operations on one stream must run within the same 62 + multiple threads, all operations on one stream must be performed
63 - `capy::strand`, or must otherwise never run concurrently. A 63 + within the same `capy::strand` (or otherwise never run
64 - single-threaded context needs no strand. 64 + concurrently); a single-threaded context needs no strand.
65   65  
66   @see openssl_stream, wolfssl_stream 66   @see openssl_stream, wolfssl_stream
67   */ 67   */
68   class BOOST_COROSIO_DECL tls_stream 68   class BOOST_COROSIO_DECL tls_stream
69   { 69   {
70   public: 70   public:
71   /// Destroy the TLS stream. 71   /// Destroy the TLS stream.
72   virtual ~tls_stream() = default; 72   virtual ~tls_stream() = default;
73   73  
74 - /// Copy construction is disabled; copying a stream would slice the derived session. 74 + tls_stream(tls_stream const&) = delete;
75 - tls_stream(tls_stream const&) = delete;  
76 - /// Copy assignment is disabled; copying a stream would slice the derived session.  
77   tls_stream& operator=(tls_stream const&) = delete; 75   tls_stream& operator=(tls_stream const&) = delete;
78   76  
79   /** Initiate an asynchronous read operation. 77   /** Initiate an asynchronous read operation.
80   78  
81   Reads decrypted data into the provided buffer sequence. The 79   Reads decrypted data into the provided buffer sequence. The
82 - operation completes when it reads at least one byte, 80 + operation completes when at least one byte has been read,
83   or an error occurs. 81   or an error occurs.
84   82  
85   This non-virtual template wrapper satisfies the `capy::Stream` 83   This non-virtual template wrapper satisfies the `capy::Stream`
86   concept by delegating to the virtual `do_read_some`. 84   concept by delegating to the virtual `do_read_some`.
87   85  
88   @par Thread Safety 86   @par Thread Safety
89   May run concurrently with one operation in the other 87   May run concurrently with one operation in the other
90   direction, subject to the class-level threading contract. 88   direction, subject to the class-level threading contract.
91   Two concurrent operations in the same direction are 89   Two concurrent operations in the same direction are
92   undefined. 90   undefined.
93   91  
94   @param buffers The buffer sequence to read data into. 92   @param buffers The buffer sequence to read data into.
95   93  
96   @return An awaitable yielding `(error_code,std::size_t)`. 94   @return An awaitable yielding `(error_code,std::size_t)`.
97   */ 95   */
98   template<capy::MutableBufferSequence Buffers> 96   template<capy::MutableBufferSequence Buffers>
MISUBC 99   ✗ [[nodiscard]] auto read_some(Buffers const& buffers) 97   ✗ [[nodiscard]] auto read_some(Buffers const& buffers)
100   { 98   {
MISUBC 101   ✗ return do_read_some(buffers); 99   ✗ return do_read_some(buffers);
102   } 100   }
103   101  
104   /** Initiate an asynchronous write operation. 102   /** Initiate an asynchronous write operation.
105   103  
106   Encrypts and writes data from the provided buffer sequence. 104   Encrypts and writes data from the provided buffer sequence.
107 - The operation completes when it writes at least one byte, 105 + The operation completes when at least one byte has been
108 - or an error occurs. 106 + written, or an error occurs.
109   107  
110   This non-virtual template wrapper satisfies the `capy::Stream` 108   This non-virtual template wrapper satisfies the `capy::Stream`
111   concept by delegating to the virtual `do_write_some`. 109   concept by delegating to the virtual `do_write_some`.
112   110  
113   @par Thread Safety 111   @par Thread Safety
114   May run concurrently with one operation in the other 112   May run concurrently with one operation in the other
115   direction, subject to the class-level threading contract. 113   direction, subject to the class-level threading contract.
116   Two concurrent operations in the same direction are 114   Two concurrent operations in the same direction are
117   undefined. 115   undefined.
118   116  
119   @param buffers The buffer sequence containing data to write. 117   @param buffers The buffer sequence containing data to write.
120   118  
121   @return An awaitable yielding `(error_code,std::size_t)`. 119   @return An awaitable yielding `(error_code,std::size_t)`.
122   */ 120   */
123   template<capy::ConstBufferSequence Buffers> 121   template<capy::ConstBufferSequence Buffers>
MISUBC 124   ✗ [[nodiscard]] auto write_some(Buffers const& buffers) 122   ✗ [[nodiscard]] auto write_some(Buffers const& buffers)
125   { 123   {
MISUBC 126   ✗ return do_write_some(buffers); 124   ✗ return do_write_some(buffers);
127   } 125   }
128   126  
129   /** Asynchronously perform the TLS handshake. 127   /** Asynchronously perform the TLS handshake.
130   128  
131   Initiates the TLS handshake process. For client connections, 129   Initiates the TLS handshake process. For client connections,
132   this sends the ClientHello and processes the server's response. 130   this sends the ClientHello and processes the server's response.
133   For server connections, this waits for the ClientHello and 131   For server connections, this waits for the ClientHello and
134   sends the server's response. 132   sends the server's response.
135   133  
136 - A handshake attempt consumes the stream state, whether it 134 + A handshake attempt, successful or not, consumes the stream
137 - succeeds or not. A subsequent call behaves as if `reset()` ran 135 + state: a subsequent call behaves as if `reset()` had been
138 - first, and performs a fresh handshake using the current 136 + called first and performs a fresh handshake using the
139 - configuration. 137 + current configuration.
140   138  
141 - @pre The underlying stream must be connected. No other TLS 139 + @par Preconditions
142 - operation may be in progress on this stream. 140 + The underlying stream must be connected. No other TLS
  141 + operation may be in progress on this stream.
143   142  
144   @param role The handshake role, client or server. 143   @param role The handshake role, client or server.
145   144  
146   @return An awaitable yielding `(error_code)`. 145   @return An awaitable yielding `(error_code)`.
147   */ 146   */
148   [[nodiscard]] virtual capy::io_task<> handshake(tls_role role) = 0; 147   [[nodiscard]] virtual capy::io_task<> handshake(tls_role role) = 0;
149   148  
150   /** Asynchronously perform a graceful TLS shutdown. 149   /** Asynchronously perform a graceful TLS shutdown.
151   150  
152   Initiates the TLS shutdown sequence by sending a close_notify 151   Initiates the TLS shutdown sequence by sending a close_notify
153   alert and waiting for the peer's close_notify response. 152   alert and waiting for the peer's close_notify response.
154   153  
155 - @pre A handshake must have completed successfully. May overlap 154 + @par Preconditions
156 - a pending read. No concurrent write may be in progress. 155 + A handshake must have completed successfully. May overlap
  156 + a pending read. No concurrent write may be in progress.
157   157  
158   @par Postconditions 158   @par Postconditions
159 - If the transport ends before the peer's close_notify arrives, 159 + If the transport ends before the peer's close_notify is
160 - the result is `capy::error::stream_truncated`, not success. An 160 + received, the result is `capy::error::stream_truncated`, not
161 - unannounced close is indistinguishable from a truncation attack, 161 + success: an unannounced close is indistinguishable from a
162 - so it must not be reported as a clean shutdown. A shutdown 162 + truncation attack and must not be reported as a clean
163 - stopped mid-flight reports canceled. Any other transport 163 + shutdown. A shutdown stopped mid-flight reports canceled;
164 - error propagates unchanged. 164 + any other transport error propagates unchanged.
165   165  
166   @return An awaitable yielding `(error_code)`. 166   @return An awaitable yielding `(error_code)`.
167   */ 167   */
168   [[nodiscard]] virtual capy::io_task<> shutdown() = 0; 168   [[nodiscard]] virtual capy::io_task<> shutdown() = 0;
169   169  
170   /** Reset TLS session state for reuse. 170   /** Reset TLS session state for reuse.
171   171  
172   Releases TLS session state including session keys and peer 172   Releases TLS session state including session keys and peer
173   certificates, returning the stream to a state where 173   certificates, returning the stream to a state where
174   `handshake()` can be called again. Internal memory 174   `handshake()` can be called again. Internal memory
175   allocations (I/O buffers) are preserved. 175   allocations (I/O buffers) are preserved.
176   176  
177   Calling `handshake()` on a previously-used stream 177   Calling `handshake()` on a previously-used stream
178   implicitly performs a reset first, so explicit calls 178   implicitly performs a reset first, so explicit calls
179   are only needed to eagerly release session state. 179   are only needed to eagerly release session state.
180   180  
181 - @pre No TLS operation (handshake, read, write, shutdown) is 181 + @par Preconditions
182 - in progress. 182 + No TLS operation (handshake, read, write, shutdown) is
  183 + in progress.
183   184  
184   @par Thread Safety 185   @par Thread Safety
185   Not thread safe. The caller must ensure no concurrent 186   Not thread safe. The caller must ensure no concurrent
186   operations are in progress on this stream. 187   operations are in progress on this stream.
187   188  
188   @note If called mid-session before `shutdown()`, pending 189   @note If called mid-session before `shutdown()`, pending
189 - TLS data is discarded and the peer observes a 190 + TLS data is discarded and the peer will observe a
190   truncated stream. 191   truncated stream.
191   */ 192   */
192   virtual void reset() = 0; 193   virtual void reset() = 0;
193   194  
194   /** Set the peer hostname for SNI and certificate verification. 195   /** Set the peer hostname for SNI and certificate verification.
195   196  
196   Configures the hostname sent in the TLS Server Name 197   Configures the hostname sent in the TLS Server Name
197   Indication extension and matched against the peer 198   Indication extension and matched against the peer
198   certificate during verification. The value takes effect 199   certificate during verification. The value takes effect
199   at the next `handshake()`; an established session is not 200   at the next `handshake()`; an established session is not
200   affected. It persists across `reset()`, so a stream reused 201   affected. It persists across `reset()`, so a stream reused
201   to reach a different host must set the new name before 202   to reach a different host must set the new name before
202   handshaking again. 203   handshaking again.
203   204  
204   An empty hostname (the default) disables SNI and hostname 205   An empty hostname (the default) disables SNI and hostname
205   verification. 206   verification.
206   207  
207   If `hostname` is an IP literal (IPv4 or IPv6), it is matched 208   If `hostname` is an IP literal (IPv4 or IPv6), it is matched
208 - against the certificate's iPAddress entries instead of its DNS 209 + against the certificate's iPAddress entries instead of its
209 - names. No SNI is sent, because RFC 6066 excludes literals. 210 + DNS names, and no SNI is sent (RFC 6066 excludes literals).
210   A backend build that cannot match iPAddress entries fails the 211   A backend build that cannot match iPAddress entries fails the
211   handshake with `std::errc::function_not_supported` rather 212   handshake with `std::errc::function_not_supported` rather
212   than skip verification. 213   than skip verification.
213   214  
214   @par Postconditions 215   @par Postconditions
215   The next `handshake()` uses `hostname` for SNI and 216   The next `handshake()` uses `hostname` for SNI and
216   certificate verification, or neither if it is empty. 217   certificate verification, or neither if it is empty.
217   218  
218   @note The hostname is used for client handshakes only; 219   @note The hostname is used for client handshakes only;
219   it is ignored when handshaking as a server. 220   it is ignored when handshaking as a server.
220   221  
221   @param hostname The peer hostname, or empty to disable. 222   @param hostname The peer hostname, or empty to disable.
222   */ 223   */
223   virtual void set_hostname(std::string_view hostname) = 0; 224   virtual void set_hostname(std::string_view hostname) = 0;
224   225  
225   /** Return a reference to the underlying stream. 226   /** Return a reference to the underlying stream.
226   227  
227   Provides access to the type-erased underlying stream for 228   Provides access to the type-erased underlying stream for
228   operations like cancellation or accessing native handles. 229   operations like cancellation or accessing native handles.
229   230  
230   @warning Do not reseat (assign to) the returned reference. 231   @warning Do not reseat (assign to) the returned reference.
231   The TLS implementation holds internal state bound to 232   The TLS implementation holds internal state bound to
232   the original stream. Replacing it causes undefined 233   the original stream. Replacing it causes undefined
233   behavior. 234   behavior.
234   235  
235   @return Reference to the wrapped stream. 236   @return Reference to the wrapped stream.
236   */ 237   */
237   virtual capy::any_stream& next_layer() noexcept = 0; 238   virtual capy::any_stream& next_layer() noexcept = 0;
238   239  
239   /** Return a const reference to the underlying stream. 240   /** Return a const reference to the underlying stream.
240   241  
241   @return Const reference to the wrapped stream. 242   @return Const reference to the wrapped stream.
242   */ 243   */
243   virtual capy::any_stream const& next_layer() const noexcept = 0; 244   virtual capy::any_stream const& next_layer() const noexcept = 0;
244   245  
245   /** Return the name of the TLS backend. 246   /** Return the name of the TLS backend.
246   247  
247   @return A string identifying the TLS implementation, 248   @return A string identifying the TLS implementation,
248   such as "openssl" or "wolfssl". 249   such as "openssl" or "wolfssl".
249   */ 250   */
250   virtual std::string_view name() const noexcept = 0; 251   virtual std::string_view name() const noexcept = 0;
251   252  
252   /** Return the ALPN protocol negotiated during the handshake. 253   /** Return the ALPN protocol negotiated during the handshake.
253   254  
254   Application-Layer Protocol Negotiation selects a single 255   Application-Layer Protocol Negotiation selects a single
255   application protocol (for example `"h2"` or `"http/1.1"`) 256   application protocol (for example `"h2"` or `"http/1.1"`)
256   during the TLS handshake, from the list supplied via 257   during the TLS handshake, from the list supplied via
257   @ref tls_context::set_alpn. 258   @ref tls_context::set_alpn.
258   259  
259 - @return The negotiated protocol, or an empty view. It is empty 260 + @return The negotiated protocol, or an empty view if no
260 - if no protocol was negotiated or ALPN was not offered. It is 261 + protocol was negotiated, ALPN was not offered, the
261 - also empty if the handshake has not completed, or if the build 262 + handshake has not completed, or the backend/build does
262 - lacks ALPN support. 263 + not support ALPN.
263   264  
264   @par Thread Safety 265   @par Thread Safety
265   Safe to call after the handshake completes; not safe to call 266   Safe to call after the handshake completes; not safe to call
266   concurrently with a handshake or reset. 267   concurrently with a handshake or reset.
267   */ 268   */
268   virtual std::string_view alpn_protocol() const noexcept 269   virtual std::string_view alpn_protocol() const noexcept
269   { 270   {
270   return {}; 271   return {};
271   } // LCOV_EXCL_LINE every concrete stream overrides this; the base default is never called 272   } // LCOV_EXCL_LINE every concrete stream overrides this; the base default is never called
272   273  
273 - /// Default construct; a derived class supplies the session.  
274   protected: 274   protected:
275   tls_stream() = default; 275   tls_stream() = default;
276   276  
277 - /** Perform the backend-specific decrypted read. 277 + /** Virtual read implementation.
278   278  
279   Derived classes override this to perform TLS decryption 279   Derived classes override this to perform TLS decryption
280   and read operations. 280   and read operations.
281   281  
282   @param buffers Buffer sequence to read into. 282   @param buffers Buffer sequence to read into.
283   283  
284   @return An awaitable yielding `(error_code,std::size_t)`. 284   @return An awaitable yielding `(error_code,std::size_t)`.
285   */ 285   */
286   virtual capy::io_task<std::size_t> do_read_some( 286   virtual capy::io_task<std::size_t> do_read_some(
287   capy::detail::mutable_buffer_array<capy::detail::max_iovec_> 287   capy::detail::mutable_buffer_array<capy::detail::max_iovec_>
288   buffers) = 0; 288   buffers) = 0;
289   289  
290 - /** Perform the backend-specific encrypted write. 290 + /** Virtual write implementation.
291   291  
292   Derived classes override this to perform TLS encryption 292   Derived classes override this to perform TLS encryption
293   and write operations. 293   and write operations.
294   294  
295   @param buffers Buffer sequence to write from. 295   @param buffers Buffer sequence to write from.
296   296  
297   @return An awaitable yielding `(error_code,std::size_t)`. 297   @return An awaitable yielding `(error_code,std::size_t)`.
298   */ 298   */
299   virtual capy::io_task<std::size_t> do_write_some( 299   virtual capy::io_task<std::size_t> do_write_some(
300   capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0; 300   capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0;
301   }; 301   };
302   302  
303   } // namespace boost::corosio 303   } // namespace boost::corosio
304   304  
305   #endif 305   #endif