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