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   /** Abstract base class for TLS streams. 41   /** Abstract base class for TLS streams.
42   42  
43   This class provides a runtime-polymorphic interface for TLS 43   This class provides a runtime-polymorphic interface for TLS
44   implementations. Derived classes (openssl_stream, wolfssl_stream) 44   implementations. Derived classes (openssl_stream, wolfssl_stream)
45   implement the virtual functions to provide backend-specific 45   implement the virtual functions to provide backend-specific
46   TLS functionality. 46   TLS functionality.
47   47  
48   Unlike @ref io_stream which represents OS-level I/O completed 48   Unlike @ref io_stream which represents OS-level I/O completed
49   by the kernel, TLS streams are coroutine-based: their operations 49   by the kernel, TLS streams are coroutine-based: their operations
50   are implemented as coroutines that orchestrate sub-operations 50   are implemented as coroutines that orchestrate sub-operations
51   on the underlying stream. 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. When the execution context runs on 61   may overlap a pending read. When the execution context runs on
62   multiple threads, all operations on one stream must be performed 62   multiple threads, all operations on one stream must be performed
63   within the same `capy::strand` (or otherwise never run 63   within the same `capy::strand` (or otherwise never run
64   concurrently); a 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   tls_stream(tls_stream const&) = delete; 74   tls_stream(tls_stream const&) = delete;
75   tls_stream& operator=(tls_stream const&) = delete; 75   tls_stream& operator=(tls_stream const&) = delete;
76   76  
77   /** Initiate an asynchronous read operation. 77   /** Initiate an asynchronous read operation.
78   78  
79   Reads decrypted data into the provided buffer sequence. The 79   Reads decrypted data into the provided buffer sequence. The
80   operation completes when at least one byte has been read, 80   operation completes when at least one byte has been read,
81   or an error occurs. 81   or an error occurs.
82   82  
83   This non-virtual template wrapper satisfies the `capy::Stream` 83   This non-virtual template wrapper satisfies the `capy::Stream`
84   concept by delegating to the virtual `do_read_some`. 84   concept by delegating to the virtual `do_read_some`.
85   85  
86   @par Thread Safety 86   @par Thread Safety
87   May run concurrently with one operation in the other 87   May run concurrently with one operation in the other
88   direction, subject to the class-level threading contract. 88   direction, subject to the class-level threading contract.
89   Two concurrent operations in the same direction are 89   Two concurrent operations in the same direction are
90   undefined. 90   undefined.
91   91  
92   @param buffers The buffer sequence to read data into. 92   @param buffers The buffer sequence to read data into.
93   93  
94   @return An awaitable yielding `(error_code,std::size_t)`. 94   @return An awaitable yielding `(error_code,std::size_t)`.
95   */ 95   */
96   template<capy::MutableBufferSequence Buffers> 96   template<capy::MutableBufferSequence Buffers>
MISUBC 97   auto read_some(Buffers const& buffers) 97   auto read_some(Buffers const& buffers)
98   { 98   {
MISUBC 99   return do_read_some(buffers); 99   return do_read_some(buffers);
100   } 100   }
101   101  
102   /** Initiate an asynchronous write operation. 102   /** Initiate an asynchronous write operation.
103   103  
104   Encrypts and writes data from the provided buffer sequence. 104   Encrypts and writes data from the provided buffer sequence.
105   The operation completes when at least one byte has been 105   The operation completes when at least one byte has been
106   written, or an error occurs. 106   written, or an error occurs.
107   107  
108   This non-virtual template wrapper satisfies the `capy::Stream` 108   This non-virtual template wrapper satisfies the `capy::Stream`
109   concept by delegating to the virtual `do_write_some`. 109   concept by delegating to the virtual `do_write_some`.
110   110  
111   @par Thread Safety 111   @par Thread Safety
112   May run concurrently with one operation in the other 112   May run concurrently with one operation in the other
113   direction, subject to the class-level threading contract. 113   direction, subject to the class-level threading contract.
114   Two concurrent operations in the same direction are 114   Two concurrent operations in the same direction are
115   undefined. 115   undefined.
116   116  
117   @param buffers The buffer sequence containing data to write. 117   @param buffers The buffer sequence containing data to write.
118   118  
119   @return An awaitable yielding `(error_code,std::size_t)`. 119   @return An awaitable yielding `(error_code,std::size_t)`.
120   */ 120   */
121   template<capy::ConstBufferSequence Buffers> 121   template<capy::ConstBufferSequence Buffers>
MISUBC 122   auto write_some(Buffers const& buffers) 122   auto write_some(Buffers const& buffers)
123   { 123   {
MISUBC 124   return do_write_some(buffers); 124   return do_write_some(buffers);
125   } 125   }
126   126  
127   /** Asynchronously perform the TLS handshake. 127   /** Asynchronously perform the TLS handshake.
128   128  
129   Initiates the TLS handshake process. For client connections, 129   Initiates the TLS handshake process. For client connections,
130   this sends the ClientHello and processes the server's response. 130   this sends the ClientHello and processes the server's response.
131   For server connections, this waits for the ClientHello and 131   For server connections, this waits for the ClientHello and
132   sends the server's response. 132   sends the server's response.
133   133  
134   A handshake attempt, successful or not, consumes the stream 134   A handshake attempt, successful or not, consumes the stream
135   state: a subsequent call behaves as if `reset()` had been 135   state: a subsequent call behaves as if `reset()` had been
136   called first and performs a fresh handshake using the 136   called first and performs a fresh handshake using the
137   current configuration. 137   current configuration.
138   138  
139   @par Preconditions 139   @par Preconditions
140   The underlying stream must be connected. No other TLS 140   The underlying stream must be connected. No other TLS
141   operation may be in progress on this stream. 141   operation may be in progress on this stream.
142   142  
143   @param role The handshake role, client or server. 143   @param role The handshake role, client or server.
144   144  
145   @return An awaitable yielding `(error_code)`. 145   @return An awaitable yielding `(error_code)`.
146   */ 146   */
147   virtual capy::io_task<> handshake(tls_role role) = 0; 147   virtual capy::io_task<> handshake(tls_role role) = 0;
148   148  
149   /** Asynchronously perform a graceful TLS shutdown. 149   /** Asynchronously perform a graceful TLS shutdown.
150   150  
151   Initiates the TLS shutdown sequence by sending a close_notify 151   Initiates the TLS shutdown sequence by sending a close_notify
152   alert and waiting for the peer's close_notify response. 152   alert and waiting for the peer's close_notify response.
153   153  
154   @par Preconditions 154   @par Preconditions
155   A handshake must have completed successfully. May overlap 155   A handshake must have completed successfully. May overlap
156   a pending read. No concurrent write may be in progress. 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 is 159   If the transport ends before the peer's close_notify is
160   received, the result is `capy::error::stream_truncated`, not 160   received, the result is `capy::error::stream_truncated`, not
161   success: an unannounced close is indistinguishable from a 161   success: an unannounced close is indistinguishable from a
162   truncation attack and must not be reported as a clean 162   truncation attack and must not be reported as a clean
163   shutdown. A shutdown stopped mid-flight reports canceled; 163   shutdown. A shutdown stopped mid-flight reports canceled;
164   any other transport 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   virtual capy::io_task<> shutdown() = 0; 168   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   @par Preconditions 181   @par Preconditions
182   No TLS operation (handshake, read, write, shutdown) is 182   No TLS operation (handshake, read, write, shutdown) is
183   in progress. 183   in progress.
184   184  
185   @par Thread Safety 185   @par Thread Safety
186   Not thread safe. The caller must ensure no concurrent 186   Not thread safe. The caller must ensure no concurrent
187   operations are in progress on this stream. 187   operations are in progress on this stream.
188   188  
189   @note If called mid-session before `shutdown()`, pending 189   @note If called mid-session before `shutdown()`, pending
190   TLS data is discarded and the peer will observe a 190   TLS data is discarded and the peer will observe a
191   truncated stream. 191   truncated stream.
192   */ 192   */
193   virtual void reset() = 0; 193   virtual void reset() = 0;
194   194  
195   /** Set the peer hostname for SNI and certificate verification. 195   /** Set the peer hostname for SNI and certificate verification.
196   196  
197   Configures the hostname sent in the TLS Server Name 197   Configures the hostname sent in the TLS Server Name
198   Indication extension and matched against the peer 198   Indication extension and matched against the peer
199   certificate during verification. The value takes effect 199   certificate during verification. The value takes effect
200   at the next `handshake()`; an established session is not 200   at the next `handshake()`; an established session is not
201   affected. It persists across `reset()`, so a stream reused 201   affected. It persists across `reset()`, so a stream reused
202   to reach a different host must set the new name before 202   to reach a different host must set the new name before
203   handshaking again. 203   handshaking again.
204   204  
205   An empty hostname (the default) disables SNI and hostname 205   An empty hostname (the default) disables SNI and hostname
206   verification. 206   verification.
207   207  
208   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
209   against the certificate's iPAddress entries instead of its 209   against the certificate's iPAddress entries instead of its
210   DNS names, and no SNI is sent (RFC 6066 excludes literals). 210   DNS names, and no SNI is sent (RFC 6066 excludes literals).
211   A backend build that cannot match iPAddress entries fails the 211   A backend build that cannot match iPAddress entries fails the
212   handshake with `std::errc::function_not_supported` rather 212   handshake with `std::errc::function_not_supported` rather
213   than skip verification. 213   than skip verification.
214   214  
215   @par Postconditions 215   @par Postconditions
216   The next `handshake()` uses `hostname` for SNI and 216   The next `handshake()` uses `hostname` for SNI and
217   certificate verification, or neither if it is empty. 217   certificate verification, or neither if it is empty.
218   218  
219   @note The hostname is used for client handshakes only; 219   @note The hostname is used for client handshakes only;
220   it is ignored when handshaking as a server. 220   it is ignored when handshaking as a server.
221   221  
222   @param hostname The peer hostname, or empty to disable. 222   @param hostname The peer hostname, or empty to disable.
223   */ 223   */
224   virtual void set_hostname(std::string_view hostname) = 0; 224   virtual void set_hostname(std::string_view hostname) = 0;
225   225  
226   /** Return a reference to the underlying stream. 226   /** Return a reference to the underlying stream.
227   227  
228   Provides access to the type-erased underlying stream for 228   Provides access to the type-erased underlying stream for
229   operations like cancellation or accessing native handles. 229   operations like cancellation or accessing native handles.
230   230  
231   @warning Do not reseat (assign to) the returned reference. 231   @warning Do not reseat (assign to) the returned reference.
232   The TLS implementation holds internal state bound to 232   The TLS implementation holds internal state bound to
233   the original stream. Replacing it causes undefined 233   the original stream. Replacing it causes undefined
234   behavior. 234   behavior.
235   235  
236   @return Reference to the wrapped stream. 236   @return Reference to the wrapped stream.
237   */ 237   */
238   virtual capy::any_stream& next_layer() noexcept = 0; 238   virtual capy::any_stream& next_layer() noexcept = 0;
239   239  
240   /** Return a const reference to the underlying stream. 240   /** Return a const reference to the underlying stream.
241   241  
242   @return Const reference to the wrapped stream. 242   @return Const reference to the wrapped stream.
243   */ 243   */
244   virtual capy::any_stream const& next_layer() const noexcept = 0; 244   virtual capy::any_stream const& next_layer() const noexcept = 0;
245   245  
246   /** Return the name of the TLS backend. 246   /** Return the name of the TLS backend.
247   247  
248   @return A string identifying the TLS implementation, 248   @return A string identifying the TLS implementation,
249   such as "openssl" or "wolfssl". 249   such as "openssl" or "wolfssl".
250   */ 250   */
251   virtual std::string_view name() const noexcept = 0; 251   virtual std::string_view name() const noexcept = 0;
252   252  
253   /** Return the ALPN protocol negotiated during the handshake. 253   /** Return the ALPN protocol negotiated during the handshake.
254   254  
255   Application-Layer Protocol Negotiation selects a single 255   Application-Layer Protocol Negotiation selects a single
256   application protocol (for example `"h2"` or `"http/1.1"`) 256   application protocol (for example `"h2"` or `"http/1.1"`)
257   during the TLS handshake, from the list supplied via 257   during the TLS handshake, from the list supplied via
258   @ref tls_context::set_alpn. 258   @ref tls_context::set_alpn.
259   259  
260   @return The negotiated protocol, or an empty view if no 260   @return The negotiated protocol, or an empty view if no
261   protocol was negotiated, ALPN was not offered, the 261   protocol was negotiated, ALPN was not offered, the
262   handshake has not completed, or the backend/build does 262   handshake has not completed, or the backend/build does
263   not support ALPN. 263   not support ALPN.
264   264  
265   @par Thread Safety 265   @par Thread Safety
266   Safe to call after the handshake completes; not safe to call 266   Safe to call after the handshake completes; not safe to call
267   concurrently with a handshake or reset. 267   concurrently with a handshake or reset.
268   */ 268   */
269   virtual std::string_view alpn_protocol() const noexcept { return {}; } 269   virtual std::string_view alpn_protocol() const noexcept { return {}; }
270   270  
271   protected: 271   protected:
272   tls_stream() = default; 272   tls_stream() = default;
273   273  
274   /** Virtual read implementation. 274   /** Virtual read implementation.
275   275  
276   Derived classes override this to perform TLS decryption 276   Derived classes override this to perform TLS decryption
277   and read operations. 277   and read operations.
278   278  
279   @param buffers Buffer sequence to read into. 279   @param buffers Buffer sequence to read into.
280   280  
281   @return An awaitable yielding `(error_code,std::size_t)`. 281   @return An awaitable yielding `(error_code,std::size_t)`.
282   */ 282   */
283   virtual capy::io_task<std::size_t> do_read_some( 283   virtual capy::io_task<std::size_t> do_read_some(
284   capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers) = 0; 284   capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers) = 0;
285   285  
286   /** Virtual write implementation. 286   /** Virtual write implementation.
287   287  
288   Derived classes override this to perform TLS encryption 288   Derived classes override this to perform TLS encryption
289   and write operations. 289   and write operations.
290   290  
291   @param buffers Buffer sequence to write from. 291   @param buffers Buffer sequence to write from.
292   292  
293   @return An awaitable yielding `(error_code,std::size_t)`. 293   @return An awaitable yielding `(error_code,std::size_t)`.
294   */ 294   */
295   virtual capy::io_task<std::size_t> do_write_some( 295   virtual capy::io_task<std::size_t> do_write_some(
296   capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0; 296   capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0;
297   }; 297   };
298   298  
299   } // namespace boost::corosio 299   } // namespace boost::corosio
300   300  
301   #endif 301   #endif