Botan 2.19.5 Crypto and TLS for C&

asio_stream.h
Go to the documentation of this file.
1/*
2* TLS ASIO Stream
3* (C) 2018-2020 Jack Lloyd
4* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#ifndef BOTAN_ASIO_STREAM_H_
10#define BOTAN_ASIO_STREAM_H_
11
12#include <botan/build.h>
13
14// first version to be compatible with Networking TS (N4656) and boost::beast
15#include <boost/version.hpp>
16#if BOOST_VERSION >= 106600
17
18#include <botan/asio_async_ops.h>
19#include <botan/asio_context.h>
20#include <botan/asio_error.h>
21
22#include <botan/tls_callbacks.h>
23#include <botan/tls_channel.h>
24#include <botan/tls_client.h>
25#include <botan/tls_magic.h>
26#include <botan/tls_server.h>
27
28// We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>,
29// which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'.
30#define BOOST_ASIO_DISABLE_SERIAL_PORT
31#include <boost/asio.hpp>
32#include <boost/beast/core.hpp>
33
34#include <algorithm>
35#include <memory>
36#include <type_traits>
37
38 namespace Botan {
39 namespace TLS {
40
41/**
42 * @brief boost::asio compatible SSL/TLS stream
43 *
44 * @tparam StreamLayer type of the next layer, usually a network socket
45 * @tparam ChannelT type of the native_handle, defaults to Botan::TLS::Channel, only needed for testing purposes
46 */
47 template <class StreamLayer, class ChannelT = Channel>
48 class Stream
49 {
50 public:
51 //! \name construction
52 //! @{
53
54 /**
55 * @brief Construct a new Stream
56 *
57 * @param context The context parameter is used to set up the underlying native handle. Using code is
58 * responsible for lifetime management of the context and must ensure that it is available for the
59 * lifetime of the stream.
60 * @param args Arguments to be forwarded to the construction of the next layer.
61 */
62 template <typename... Args>
63 explicit Stream(Context& context, Args&& ... args)
64 : m_context(context)
65 , m_nextLayer(std::forward<Args>(args)...)
66 , m_core(*this)
67 , m_shutdown_received(false)
68 , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
69 , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
70 {}
71
72 /**
73 * @brief Construct a new Stream
74 *
75 * Convenience overload for boost::asio::ssl::stream compatibility.
76 *
77 * @param arg This argument is forwarded to the construction of the next layer.
78 * @param context The context parameter is used to set up the underlying native handle. Using code is
79 * responsible for lifetime management of the context and must ensure that is available for the
80 * lifetime of the stream.
81 */
82 template <typename Arg>
83 explicit Stream(Arg&& arg, Context& context)
84 : m_context(context)
85 , m_nextLayer(std::forward<Arg>(arg))
86 , m_core(*this)
87 , m_shutdown_received(false)
88 , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
89 , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
90 {}
91
92 virtual ~Stream() {}
93
94 Stream(Stream&& other) = default;
95 Stream& operator=(Stream&& other) = default;
96
97 Stream(const Stream& other) = delete;
98 Stream& operator=(const Stream& other) = delete;
99
100 //! @}
101 //! \name boost::asio accessor methods
102 //! @{
103
104 using next_layer_type = typename std::remove_reference<StreamLayer>::type;
105
106 const next_layer_type& next_layer() const { return m_nextLayer; }
107 next_layer_type& next_layer() { return m_nextLayer; }
108
109#if BOOST_VERSION >= 107000
110 /*
111 * From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
112 * Instead, the new free-standing functions in Beast need to be used.
113 * See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
114 */
115 using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
116
117 lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
118 const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
119#else
120 using lowest_layer_type = typename next_layer_type::lowest_layer_type;
121
122 lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
123 const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
124#endif
125
126 using executor_type = typename next_layer_type::executor_type;
127 executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
128
129 using native_handle_type = typename std::add_pointer<ChannelT>::type;
130 native_handle_type native_handle()
131 {
132 if(m_native_handle == nullptr)
133 { throw Invalid_State("Invalid handshake state"); }
134 return m_native_handle.get();
135 }
136
137 //! @}
138 //! \name configuration and callback setters
139 //! @{
140
141 /**
142 * @brief Override the tls_verify_cert_chain callback
143 *
144 * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
145 * used in the handshake.
146 * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
147 *
148 * @note This function should only be called before initiating the TLS handshake
149 */
150 void set_verify_callback(Context::Verify_Callback callback)
151 {
152 m_context.set_verify_callback(std::move(callback));
153 }
154
155 /**
156 * @brief Compatibility overload of @ref set_verify_callback
157 *
158 * @param callback the callback implementation
159 * @param ec This parameter is unused.
160 */
161 void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec)
162 {
163 BOTAN_UNUSED(ec);
164 m_context.set_verify_callback(std::move(callback));
165 }
166
167 //! @throws Not_Implemented
168 void set_verify_depth(int depth)
169 {
170 BOTAN_UNUSED(depth);
171 throw Not_Implemented("set_verify_depth is not implemented");
172 }
173
174 /**
175 * Not Implemented.
176 * @param depth the desired verification depth
177 * @param ec Will be set to `Botan::ErrorType::NotImplemented`
178 */
179 void set_verify_depth(int depth, boost::system::error_code& ec)
180 {
181 BOTAN_UNUSED(depth);
182 ec = Botan::ErrorType::NotImplemented;
183 }
184
185 //! @throws Not_Implemented
186 template <typename verify_mode>
187 void set_verify_mode(verify_mode v)
188 {
189 BOTAN_UNUSED(v);
190 throw Not_Implemented("set_verify_mode is not implemented");
191 }
192
193 /**
194 * Not Implemented.
195 * @param v the desired verify mode
196 * @param ec Will be set to `Botan::ErrorType::NotImplemented`
197 */
198 template <typename verify_mode>
199 void set_verify_mode(verify_mode v, boost::system::error_code& ec)
200 {
201 BOTAN_UNUSED(v);
202 ec = Botan::ErrorType::NotImplemented;
203 }
204
205 //! @}
206 //! \name handshake methods
207 //! @{
208
209 /**
210 * @brief Performs SSL handshaking.
211 *
212 * The function call will block until handshaking is complete or an error occurs.
213 *
214 * @param side The type of handshaking to be performed, i.e. as a client or as a server.
215 * @throws boost::system::system_error if error occured
216 */
217 void handshake(Connection_Side side)
218 {
219 boost::system::error_code ec;
220 handshake(side, ec);
221 boost::asio::detail::throw_error(ec, "handshake");
222 }
223
224 /**
225 * @brief Performs SSL handshaking.
226 *
227 * The function call will block until handshaking is complete or an error occurs.
228 *
229 * @param side The type of handshaking to be performed, i.e. as a client or as a server.
230 * @param ec Set to indicate what error occurred, if any.
231 */
232 void handshake(Connection_Side side, boost::system::error_code& ec)
233 {
234 setup_native_handle(side, ec);
235
236 if(side == CLIENT)
237 {
238 // send client hello, which was written to the send buffer on client instantiation
239 send_pending_encrypted_data(ec);
240 }
241
242 while(!native_handle()->is_active() && !ec)
243 {
244 boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
245 if(ec)
246 { return; }
247
248 process_encrypted_data(read_buffer, ec);
249
250 send_pending_encrypted_data(ec);
251 }
252 }
253
254 /**
255 * @brief Starts an asynchronous SSL handshake.
256 *
257 * This function call always returns immediately.
258 *
259 * @param side The type of handshaking to be performed, i.e. as a client or as a server.
260 * @param handler The handler to be called when the handshake operation completes.
261 * The equivalent function signature of the handler must be: void(boost::system::error_code)
262 */
263 template <typename HandshakeHandler>
264 auto async_handshake(Connection_Side side, HandshakeHandler&& handler) ->
265 BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, void(boost::system::error_code))
266 {
267 BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(HandshakeHandler, handler) type_check;
268
269 boost::system::error_code ec;
270 setup_native_handle(side, ec);
271 // If ec is set by setup_native_handle, the AsyncHandshakeOperation created below will do nothing but call the
272 // handler with the error_code set appropriately - no need to early return here.
273
274 boost::asio::async_completion<HandshakeHandler, void(boost::system::error_code)> init(handler);
275
276 detail::AsyncHandshakeOperation<typename std::decay<HandshakeHandler>::type, Stream>
277 op{std::move(init.completion_handler), *this, ec};
278
279 return init.result.get();
280 }
281
282 //! @throws Not_Implemented
283 template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
284 BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
285 void(boost::system::error_code, std::size_t))
286 async_handshake(Connection_Side side, const ConstBufferSequence& buffers,
287 BufferedHandshakeHandler&& handler)
288 {
289 BOTAN_UNUSED(side, buffers, handler);
290 BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
291 throw Not_Implemented("buffered async handshake is not implemented");
292 }
293
294 //! @}
295 //! \name shutdown methods
296 //! @{
297
298 /**
299 * @brief Shut down SSL on the stream.
300 *
301 * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
302 * or an error occurs. Note that this will not close the lowest layer.
303 *
304 * Note that this can be used in reaction of a received shutdown alert from the peer.
305 *
306 * @param ec Set to indicate what error occured, if any.
307 */
308 void shutdown(boost::system::error_code& ec)
309 {
310 try_with_error_code([&]
311 {
312 native_handle()->close();
313 }, ec);
314
315 send_pending_encrypted_data(ec);
316 }
317
318 /**
319 * @brief Shut down SSL on the stream.
320 *
321 * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
322 * or an error occurs. Note that this will not close the lowest layer.
323 *
324 * Note that this can be used in reaction of a received shutdown alert from the peer.
325 *
326 * @throws boost::system::system_error if error occured
327 */
328 void shutdown()
329 {
330 boost::system::error_code ec;
331 shutdown(ec);
332 boost::asio::detail::throw_error(ec, "shutdown");
333 }
334
335 private:
336 /**
337 * @brief Internal wrapper type to adapt the expected signature of `async_shutdown` to the completion handler
338 * signature of `AsyncWriteOperation`.
339 *
340 * This is boilerplate to ignore the `size_t` parameter that is passed to the completion handler of
341 * `AsyncWriteOperation`. Note that it needs to retain the wrapped handler's executor.
342 */
343 template <typename Handler, typename Executor>
344 struct Wrapper
345 {
346 void operator()(boost::system::error_code ec, std::size_t)
347 {
348 handler(ec);
349 }
350
351 using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
352
353 executor_type get_executor() const noexcept
354 {
355 return boost::asio::get_associated_executor(handler, io_executor);
356 }
357
358 using allocator_type = boost::asio::associated_allocator_t<Handler>;
359
360 allocator_type get_allocator() const noexcept
361 {
362 return boost::asio::get_associated_allocator(handler);
363 }
364
365 Handler handler;
366 Executor io_executor;
367 };
368
369 public:
370 /**
371 * @brief Asynchronously shut down SSL on the stream.
372 *
373 * This function call always returns immediately.
374 *
375 * Note that this can be used in reaction of a received shutdown alert from the peer.
376 *
377 * @param handler The handler to be called when the shutdown operation completes.
378 * The equivalent function signature of the handler must be: void(boost::system::error_code)
379 */
380 template <typename ShutdownHandler>
381 void async_shutdown(ShutdownHandler&& handler)
382 {
383 boost::system::error_code ec;
384 try_with_error_code([&]
385 {
386 native_handle()->close();
387 }, ec);
388 // If ec is set by native_handle->close(), the AsyncWriteOperation created below will do nothing but call the
389 // handler with the error_code set appropriately - no need to early return here.
390
391 using ShutdownHandlerWrapper = Wrapper<ShutdownHandler, typename Stream::executor_type>;
392
393 ShutdownHandlerWrapper w{std::forward<ShutdownHandler>(handler), get_executor()};
394 BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(ShutdownHandler, w) type_check;
395
396 boost::asio::async_completion<ShutdownHandlerWrapper, void(boost::system::error_code, std::size_t)>
397 init(w);
398
399 detail::AsyncWriteOperation<typename std::decay<ShutdownHandlerWrapper>::type, Stream>
400 op{std::move(init.completion_handler), *this, boost::asio::buffer_size(send_buffer())};
401
402 return init.result.get();
403 }
404
405 //! @}
406 //! \name I/O methods
407 //! @{
408
409 /**
410 * @brief Read some data from the stream.
411 *
412 * The function call will block until one or more bytes of data has been read successfully, or until an error
413 * occurs.
414 *
415 * @param buffers The buffers into which the data will be read.
416 * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
417 * has closed the connection but did not properly shut down the SSL connection.
418 * @return The number of bytes read. Returns 0 if an error occurred.
419 */
420 template <typename MutableBufferSequence>
421 std::size_t read_some(const MutableBufferSequence& buffers,
422 boost::system::error_code& ec)
423 {
424 if(has_received_data())
425 { return copy_received_data(buffers); }
426
427 boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
428 if(ec)
429 { return 0; }
430
431 process_encrypted_data(read_buffer, ec);
432
433 if(ec) // something went wrong in process_encrypted_data()
434 { return 0; }
435
436 if(shutdown_received())
437 {
438 // we just received a 'close_notify' from the peer and don't expect any more data
439 ec = boost::asio::error::eof;
440 }
441 else if(ec == boost::asio::error::eof)
442 {
443 // we did not expect this disconnection from the peer
444 ec = StreamError::StreamTruncated;
445 }
446
447 return !ec ? copy_received_data(buffers) : 0;
448 }
449
450 /**
451 * @brief Read some data from the stream.
452 *
453 * The function call will block until one or more bytes of data has been read successfully, or until an error
454 * occurs.
455 *
456 * @param buffers The buffers into which the data will be read.
457 * @return The number of bytes read. Returns 0 if an error occurred.
458 * @throws boost::system::system_error if error occured
459 */
460 template <typename MutableBufferSequence>
461 std::size_t read_some(const MutableBufferSequence& buffers)
462 {
463 boost::system::error_code ec;
464 auto const n = read_some(buffers, ec);
465 boost::asio::detail::throw_error(ec, "read_some");
466 return n;
467 }
468
469 /**
470 * @brief Write some data to the stream.
471 *
472 * The function call will block until one or more bytes of data has been written successfully, or until an error
473 * occurs.
474 *
475 * @param buffers The data to be written.
476 * @param ec Set to indicate what error occurred, if any.
477 * @return The number of bytes processed from the input buffers.
478 */
479 template <typename ConstBufferSequence>
480 std::size_t write_some(const ConstBufferSequence& buffers,
481 boost::system::error_code& ec)
482 {
483 tls_encrypt(buffers, ec);
484 send_pending_encrypted_data(ec);
485 return !ec ? boost::asio::buffer_size(buffers) : 0;
486 }
487
488 /**
489 * @brief Write some data to the stream.
490 *
491 * The function call will block until one or more bytes of data has been written successfully, or until an error
492 * occurs.
493 *
494 * @param buffers The data to be written.
495 * @return The number of bytes written.
496 * @throws boost::system::system_error if error occured
497 */
498 template <typename ConstBufferSequence>
499 std::size_t write_some(const ConstBufferSequence& buffers)
500 {
501 boost::system::error_code ec;
502 auto const n = write_some(buffers, ec);
503 boost::asio::detail::throw_error(ec, "write_some");
504 return n;
505 }
506
507 /**
508 * @brief Start an asynchronous write. The function call always returns immediately.
509 *
510 * @param buffers The data to be written.
511 * @param handler The handler to be called when the write operation completes. Copies will be made of the handler
512 * as required. The equivalent function signature of the handler must be:
513 * void(boost::system::error_code, std::size_t)
514 */
515 template <typename ConstBufferSequence, typename WriteHandler>
516 auto async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler) ->
517 BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
518 void(boost::system::error_code, std::size_t))
519 {
520 BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
521
522 boost::asio::async_completion<WriteHandler, void(boost::system::error_code, std::size_t)> init(handler);
523
524 boost::system::error_code ec;
525 tls_encrypt(buffers, ec);
526 if(ec)
527 {
528 // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
529 // AsyncWriteOperation call the handler with the error_code set
530 consume_send_buffer(m_send_buffer.size());
531 detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
532 op{std::move(init.completion_handler), *this, std::size_t(0), ec};
533 return init.result.get();
534 }
535
536 detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
537 op{std::move(init.completion_handler), *this, boost::asio::buffer_size(buffers)};
538
539 return init.result.get();
540 }
541
542 /**
543 * @brief Start an asynchronous read. The function call always returns immediately.
544 *
545 * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
546 * necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
547 * that they remain valid until the handler is called.
548 * @param handler The handler to be called when the read operation completes. The equivalent function signature of
549 * the handler must be:
550 * void(boost::system::error_code, std::size_t)
551 */
552 template <typename MutableBufferSequence, typename ReadHandler>
553 auto async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler) ->
554 BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
555 void(boost::system::error_code, std::size_t))
556 {
557 BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
558
559 boost::asio::async_completion<ReadHandler, void(boost::system::error_code, std::size_t)> init(handler);
560
561 detail::AsyncReadOperation<typename std::decay<ReadHandler>::type, Stream, MutableBufferSequence>
562 op{std::move(init.completion_handler), *this, buffers};
563 return init.result.get();
564 }
565
566 //! @}
567
568 //! @brief Indicates whether a close_notify alert has been received from the peer.
569 bool shutdown_received() const
570 {
571 return m_shutdown_received;
572 }
573
574 protected:
575 template <class H, class S, class M, class A> friend class detail::AsyncReadOperation;
576 template <class H, class S, class A> friend class detail::AsyncWriteOperation;
577 template <class H, class S, class A> friend class detail::AsyncHandshakeOperation;
578
579 /**
580 * @brief Helper class that implements Botan::TLS::Callbacks
581 *
582 * This class is provided to the stream's native_handle (Botan::TLS::Channel) and implements the callback
583 * functions triggered by the native_handle.
584 *
585 * @param receive_buffer reference to the buffer where decrypted data should be placed
586 * @param send_buffer reference to the buffer where encrypted data should be placed
587 */
588 class StreamCore : public Botan::TLS::Callbacks
589 {
590 public:
591 StreamCore(Stream& stream) : m_stream(stream) {}
592
593 virtual ~StreamCore() {}
594
595 void tls_emit_data(const uint8_t data[], std::size_t size) override
596 {
597 m_stream.m_send_buffer.commit(
598 boost::asio::buffer_copy(m_stream.m_send_buffer.prepare(size), boost::asio::buffer(data, size))
599 );
600 }
601
602 void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
603 {
604 m_stream.m_receive_buffer.commit(
605 boost::asio::buffer_copy(m_stream.m_receive_buffer.prepare(size), boost::asio::const_buffer(data, size))
606 );
607 }
608
609 void tls_alert(Botan::TLS::Alert alert) override
610 {
611 if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
612 {
613 m_stream.set_shutdown_received();
614 // Channel::process_alert will automatically write the corresponding close_notify response to the
615 // send_buffer and close the native_handle after this function returns.
616 }
617 }
618
619 std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
620 {
621 return std::chrono::milliseconds(1000);
622 }
623
624 bool tls_session_established(const Botan::TLS::Session&) override
625 {
626 // TODO: it should be possible to configure this in the using application (via callback?)
627 return true;
628 }
629
630 void tls_verify_cert_chain(
631 const std::vector<X509_Certificate>& cert_chain,
632 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
633 const std::vector<Certificate_Store*>& trusted_roots,
634 Usage_Type usage,
635 const std::string& hostname,
636 const TLS::Policy& policy) override
637 {
638 if(m_stream.m_context.has_verify_callback())
639 {
640 m_stream.m_context.get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
641 }
642 else
643 {
644 Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
645 }
646 }
647
648 private:
649 Stream& m_stream;
650 };
651
652 const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
653 boost::asio::const_buffer send_buffer() const { return m_send_buffer.data(); }
654
655 //! @brief Check if decrypted data is available in the receive buffer
656 bool has_received_data() const { return m_receive_buffer.size() > 0; }
657
658 //! @brief Copy decrypted data into the user-provided buffer
659 template <typename MutableBufferSequence>
660 std::size_t copy_received_data(MutableBufferSequence buffers)
661 {
662 // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
663 // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
664 // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
665 // than the decrypted record, so this optimization might not be worth the additional complexity.
666 const auto copiedBytes = boost::asio::buffer_copy(buffers, m_receive_buffer.data());
667 m_receive_buffer.consume(copiedBytes);
668 return copiedBytes;
669 }
670
671 //! @brief Check if encrypted data is available in the send buffer
672 bool has_data_to_send() const { return m_send_buffer.size() > 0; }
673
674 //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
675 void consume_send_buffer(std::size_t bytesConsumed) { m_send_buffer.consume(bytesConsumed); }
676
677 // This is a helper construct to allow mocking the native_handle in test code. It is activated by explicitly
678 // specifying a (mocked) channel type template parameter when constructing the stream and does not attempt to
679 // instantiate the native_handle.
680 // Note: once we have C++17 we can achieve this much more elegantly using constexpr if.
681 template<class T = ChannelT>
682 typename std::enable_if<!std::is_same<Channel, T>::value>::type
683 setup_native_handle(Connection_Side, boost::system::error_code&) {}
684
685 /**
686 * @brief Create the native handle.
687 *
688 * Depending on the desired connection side, this function will create a Botan::TLS::Client or a
689 * Botan::TLS::Server.
690 *
691 * @param side The desired connection side (client or server)
692 * @param ec Set to indicate what error occurred, if any.
693 */
694 template<class T = ChannelT>
695 typename std::enable_if<std::is_same<Channel, T>::value>::type
696 setup_native_handle(Connection_Side side, boost::system::error_code& ec)
697 {
698 try_with_error_code([&]
699 {
700 if(side == CLIENT)
701 {
702 m_native_handle = std::unique_ptr<Client>(
703 new Client(m_core,
704 m_context.m_session_manager,
705 m_context.m_credentials_manager,
706 m_context.m_policy,
707 m_context.m_rng,
708 m_context.m_server_info,
709 Protocol_Version::latest_tls_version()));
710 }
711 else
712 {
713 m_native_handle = std::unique_ptr<Server>(
714 new Server(m_core,
715 m_context.m_session_manager,
716 m_context.m_credentials_manager,
717 m_context.m_policy,
718 m_context.m_rng,
719 false /* no DTLS */));
720 }
721 }, ec);
722 }
723
724 /** @brief Synchronously write encrypted data from the send buffer to the next layer.
725 *
726 * If this function is called with an error code other than 'Success', it will do nothing and return 0.
727 *
728 * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
729 * has closed the connection but did not properly shut down the SSL connection.
730 * @return The number of bytes written.
731 */
732 size_t send_pending_encrypted_data(boost::system::error_code& ec)
733 {
734 if(ec)
735 { return 0; }
736
737 auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
738 consume_send_buffer(writtenBytes);
739
740 if(ec == boost::asio::error::eof && !shutdown_received())
741 {
742 // transport layer was closed by peer without receiving 'close_notify'
743 ec = StreamError::StreamTruncated;
744 }
745
746 return writtenBytes;
747 }
748
749 /**
750 * @brief Pass plaintext data to the native handle for processing.
751 *
752 * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
753 */
754 template <typename ConstBufferSequence>
755 void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec)
756 {
757 // NOTE: This is not asynchronous: it encrypts the data synchronously.
758 // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
759 // but is not actually written to the wire, yet.
760 for(auto it = boost::asio::buffer_sequence_begin(buffers);
761 !ec && it != boost::asio::buffer_sequence_end(buffers);
762 it++)
763 {
764 const boost::asio::const_buffer buffer = *it;
765 try_with_error_code([&]
766 {
767 native_handle()->send(static_cast<const uint8_t*>(buffer.data()), buffer.size());
768 }, ec);
769 }
770 }
771
772 /**
773 * @brief Pass encrypted data to the native handle for processing.
774 *
775 * If an exception occurs while processing the data, an error code will be set.
776 *
777 * @param read_buffer Input buffer containing the encrypted data.
778 * @param ec Set to indicate what error occurred, if any.
779 */
780 void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec)
781 {
782 try_with_error_code([&]
783 {
784 native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
785 }, ec);
786 }
787
788 //! @brief Catch exceptions and set an error_code
789 template <typename Fun>
790 void try_with_error_code(Fun f, boost::system::error_code& ec)
791 {
792 try
793 {
794 f();
795 }
796 catch(const TLS_Exception& e)
797 {
798 ec = e.type();
799 }
800 catch(const Botan::Exception& e)
801 {
802 ec = e.error_type();
803 }
804 catch(const std::exception&)
805 {
806 ec = Botan::ErrorType::Unknown;
807 }
808 }
809
810 void set_shutdown_received()
811 {
812 m_shutdown_received = true;
813 }
814
815 Context& m_context;
816 StreamLayer m_nextLayer;
817
818 boost::beast::flat_buffer m_receive_buffer;
819 boost::beast::flat_buffer m_send_buffer;
820
821 StreamCore m_core;
822 std::unique_ptr<ChannelT> m_native_handle;
823
824 bool m_shutdown_received;
825
826 // Buffer space used to read input intended for the core
827 std::vector<uint8_t> m_input_buffer_space;
828 const boost::asio::mutable_buffer m_input_buffer;
829 };
830
831} // namespace TLS
832} // namespace Botan
833
834#endif // BOOST_VERSION
835#endif // BOTAN_ASIO_STREAM_H_
BOTAN_UNUSED
#define BOTAN_UNUSED(...)
Definition: assert.h:142
Botan::Exception
Definition: exceptn.h:86
Botan::Invalid_State
Definition: exceptn.h:216
Botan::TLS::Alert
Definition: tls_alert.h:22
Botan::TLS::Alert::CLOSE_NOTIFY
@ CLOSE_NOTIFY
Definition: tls_alert.h:28
Botan::TLS::Alert::type
Type type() const
Definition: tls_alert.h:80
Botan::TLS::Callbacks
Definition: tls_callbacks.h:41
Botan::TLS::Callbacks::tls_verify_cert_chain
virtual void tls_verify_cert_chain(const std::vector< X509_Certificate > &cert_chain, const std::vector< std::shared_ptr< const OCSP::Response > > &ocsp_responses, const std::vector< Certificate_Store * > &trusted_roots, Usage_Type usage, const std::string &hostname, const TLS::Policy &policy)
Definition: tls_callbacks.cpp:53
Botan::TLS::Client
Definition: tls_client.h:25
Botan::TLS::Context
Definition: asio_context.h:45
Botan::TLS::Context::m_credentials_manager
Credentials_Manager & m_credentials_manager
Definition: asio_context.h:107
Botan::TLS::Context::has_verify_callback
bool has_verify_callback() const
Definition: asio_context.h:89
Botan::TLS::Context::m_server_info
Server_Information m_server_info
Definition: asio_context.h:112
Botan::TLS::Context::m_rng
RandomNumberGenerator & m_rng
Definition: asio_context.h:108
Botan::TLS::Context::set_verify_callback
void set_verify_callback(Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Definition: asio_context.h:84
Botan::TLS::Context::get_verify_callback
const Verify_Callback & get_verify_callback() const
Definition: asio_context.h:94
Botan::TLS::Context::m_policy
Policy & m_policy
Definition: asio_context.h:110
Botan::TLS::Context::m_session_manager
Session_Manager & m_session_manager
Definition: asio_context.h:109
Botan::TLS::Context::Verify_Callback
detail::fn_signature_helper< decltype(&Callbacks::tls_verify_cert_chain)>::type Verify_Callback
Definition: asio_context.h:54
Botan::TLS::Policy
Definition: tls_policy.h:29
Botan::TLS::Protocol_Version::latest_tls_version
static Protocol_Version latest_tls_version()
Definition: tls_version.h:36
Botan::TLS::Server
Definition: tls_server.h:27
Botan::TLS::Session
Definition: tls_session.h:28
Botan::TLS::Stream::StreamCore
Helper class that implements Botan::TLS::Callbacks.
Definition: asio_stream.h:589
Botan::TLS::Stream::StreamCore::tls_record_received
void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:602
Botan::TLS::Stream::StreamCore::~StreamCore
virtual ~StreamCore()
Definition: asio_stream.h:593
Botan::TLS::Stream::StreamCore::tls_session_established
bool tls_session_established(const Botan::TLS::Session &) override
Definition: asio_stream.h:624
Botan::TLS::Stream::StreamCore::tls_emit_data
void tls_emit_data(const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:595
Botan::TLS::Stream::StreamCore::tls_verify_cert_chain
void tls_verify_cert_chain(const std::vector< X509_Certificate > &cert_chain, const std::vector< std::shared_ptr< const OCSP::Response > > &ocsp_responses, const std::vector< Certificate_Store * > &trusted_roots, Usage_Type usage, const std::string &hostname, const TLS::Policy &policy) override
Definition: asio_stream.h:630
Botan::TLS::Stream::StreamCore::tls_verify_cert_chain_ocsp_timeout
std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
Definition: asio_stream.h:619
Botan::TLS::Stream::StreamCore::StreamCore
StreamCore(Stream &stream)
Definition: asio_stream.h:591
Botan::TLS::Stream::StreamCore::tls_alert
void tls_alert(Botan::TLS::Alert alert) override
Definition: asio_stream.h:609
Botan::TLS::Stream
boost::asio compatible SSL/TLS stream
Definition: asio_stream.h:49
Botan::TLS::Stream::Stream
Stream(Stream &&other)=default
Botan::TLS::Stream::native_handle
native_handle_type native_handle()
Definition: asio_stream.h:130
Botan::TLS::Stream::copy_received_data
std::size_t copy_received_data(MutableBufferSequence buffers)
Copy decrypted data into the user-provided buffer.
Definition: asio_stream.h:660
Botan::TLS::Stream::~Stream
virtual ~Stream()
Definition: asio_stream.h:92
Botan::TLS::Stream::m_input_buffer
const boost::asio::mutable_buffer m_input_buffer
Definition: asio_stream.h:828
Botan::TLS::Stream::set_verify_mode
void set_verify_mode(verify_mode v, boost::system::error_code &ec)
Definition: asio_stream.h:199
Botan::TLS::Stream::try_with_error_code
void try_with_error_code(Fun f, boost::system::error_code &ec)
Catch exceptions and set an error_code.
Definition: asio_stream.h:790
Botan::TLS::Stream::set_verify_mode
void set_verify_mode(verify_mode v)
Definition: asio_stream.h:187
Botan::TLS::Stream::operator=
Stream & operator=(const Stream &other)=delete
Botan::TLS::Stream::m_receive_buffer
boost::beast::flat_buffer m_receive_buffer
Definition: asio_stream.h:818
Botan::TLS::Stream::async_read_some
auto async_read_some(const MutableBufferSequence &buffers, ReadHandler &&handler) -> BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void(boost::system::error_code, std::size_t))
Start an asynchronous read. The function call always returns immediately.
Definition: asio_stream.h:553
Botan::TLS::Stream::lowest_layer
lowest_layer_type & lowest_layer()
Definition: asio_stream.h:122
Botan::TLS::Stream::buffers
const ConstBufferSequence & buffers
Definition: asio_stream.h:286
Botan::TLS::Stream::setup_native_handle
std::enable_if<!std::is_same< Channel, T >::value >::type setup_native_handle(Connection_Side, boost::system::error_code &)
Definition: asio_stream.h:683
Botan::TLS::Stream::Stream
Stream(Context &context, Args &&... args)
Construct a new Stream.
Definition: asio_stream.h:63
Botan::TLS::Stream::set_verify_depth
void set_verify_depth(int depth)
Definition: asio_stream.h:168
Botan::TLS::Stream::next_layer
const next_layer_type & next_layer() const
Definition: asio_stream.h:106
Botan::TLS::Stream::m_native_handle
std::unique_ptr< ChannelT > m_native_handle
Definition: asio_stream.h:822
Botan::TLS::Stream::Stream
Stream(Arg &&arg, Context &context)
Construct a new Stream.
Definition: asio_stream.h:83
Botan::TLS::Stream::consume_send_buffer
void consume_send_buffer(std::size_t bytesConsumed)
Mark bytes in the send buffer as consumed, removing them from the buffer.
Definition: asio_stream.h:675
Botan::TLS::Stream::setup_native_handle
std::enable_if< std::is_same< Channel, T >::value >::type setup_native_handle(Connection_Side side, boost::system::error_code &ec)
Create the native handle.
Definition: asio_stream.h:696
Botan::TLS::Stream::executor_type
typename next_layer_type::executor_type executor_type
Definition: asio_stream.h:126
Botan::TLS::Stream::send_buffer
boost::asio::const_buffer send_buffer() const
Definition: asio_stream.h:653
Botan::TLS::Stream::handshake
void handshake(Connection_Side side)
Performs SSL handshaking.
Definition: asio_stream.h:217
Botan::TLS::Stream::has_received_data
bool has_received_data() const
Check if decrypted data is available in the receive buffer.
Definition: asio_stream.h:656
Botan::TLS::Stream::set_verify_callback
void set_verify_callback(Context::Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Definition: asio_stream.h:150
Botan::TLS::Stream::native_handle_type
typename std::add_pointer< ChannelT >::type native_handle_type
Definition: asio_stream.h:129
Botan::TLS::Stream::m_shutdown_received
bool m_shutdown_received
Definition: asio_stream.h:824
Botan::TLS::Stream::next_layer
next_layer_type & next_layer()
Definition: asio_stream.h:107
Botan::TLS::Stream::write_some
std::size_t write_some(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Write some data to the stream.
Definition: asio_stream.h:480
Botan::TLS::Stream::input_buffer
const boost::asio::mutable_buffer & input_buffer()
Definition: asio_stream.h:652
Botan::TLS::Stream::process_encrypted_data
void process_encrypted_data(const boost::asio::const_buffer &read_buffer, boost::system::error_code &ec)
Pass encrypted data to the native handle for processing.
Definition: asio_stream.h:780
Botan::TLS::Stream::get_executor
executor_type get_executor() noexcept
Definition: asio_stream.h:127
Botan::TLS::Stream::set_verify_callback
void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code &ec)
Compatibility overload of set_verify_callback.
Definition: asio_stream.h:161
Botan::TLS::Stream::next_layer_type
typename std::remove_reference< StreamLayer >::type next_layer_type
Definition: asio_stream.h:104
Botan::TLS::Stream::set_shutdown_received
void set_shutdown_received()
Definition: asio_stream.h:810
Botan::TLS::Stream::read_some
std::size_t read_some(const MutableBufferSequence &buffers)
Read some data from the stream.
Definition: asio_stream.h:461
Botan::TLS::Stream::write_some
std::size_t write_some(const ConstBufferSequence &buffers)
Write some data to the stream.
Definition: asio_stream.h:499
Botan::TLS::Stream::Stream
Stream(const Stream &other)=delete
Botan::TLS::Stream::operator=
Stream & operator=(Stream &&other)=default
Botan::TLS::Stream::send_pending_encrypted_data
size_t send_pending_encrypted_data(boost::system::error_code &ec)
Synchronously write encrypted data from the send buffer to the next layer.
Definition: asio_stream.h:732
Botan::TLS::Stream::lowest_layer_type
typename next_layer_type::lowest_layer_type lowest_layer_type
Definition: asio_stream.h:120
Botan::TLS::Stream::async_handshake
auto async_handshake(Connection_Side side, HandshakeHandler &&handler) ->
Starts an asynchronous SSL handshake.
Definition: asio_stream.h:264
Botan::TLS::Stream::handler
const ConstBufferSequence BufferedHandshakeHandler && handler
Definition: asio_stream.h:288
Botan::TLS::Stream::tls_encrypt
void tls_encrypt(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Pass plaintext data to the native handle for processing.
Definition: asio_stream.h:755
Botan::TLS::Stream::read_some
std::size_t read_some(const MutableBufferSequence &buffers, boost::system::error_code &ec)
Read some data from the stream.
Definition: asio_stream.h:421
Botan::TLS::Stream::set_verify_depth
void set_verify_depth(int depth, boost::system::error_code &ec)
Definition: asio_stream.h:179
Botan::TLS::Stream::m_input_buffer_space
std::vector< uint8_t > m_input_buffer_space
Definition: asio_stream.h:827
Botan::TLS::Stream::shutdown_received
bool shutdown_received() const
Indicates whether a close_notify alert has been received from the peer.
Definition: asio_stream.h:569
Botan::TLS::Stream::shutdown
void shutdown()
Shut down SSL on the stream.
Definition: asio_stream.h:328
Botan::TLS::Stream::m_nextLayer
StreamLayer m_nextLayer
Definition: asio_stream.h:816
Botan::TLS::Stream::m_context
Context & m_context
Definition: asio_stream.h:815
Botan::TLS::Stream::handshake
void handshake(Connection_Side side, boost::system::error_code &ec)
Performs SSL handshaking.
Definition: asio_stream.h:232
Botan::TLS::Stream::shutdown
void shutdown(boost::system::error_code &ec)
Shut down SSL on the stream.
Definition: asio_stream.h:308
Botan::TLS::Stream::BOOST_ASIO_HANDSHAKE_HANDLER_CHECK
BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check
Botan::TLS::Stream::async_write_some
auto async_write_some(const ConstBufferSequence &buffers, WriteHandler &&handler) -> BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, void(boost::system::error_code, std::size_t))
Start an asynchronous write. The function call always returns immediately.
Definition: asio_stream.h:516
Botan::TLS::Stream::lowest_layer
const lowest_layer_type & lowest_layer() const
Definition: asio_stream.h:123
Botan::TLS::Stream::m_send_buffer
boost::beast::flat_buffer m_send_buffer
Definition: asio_stream.h:819
Botan::TLS::Stream::Not_Implemented
throw Not_Implemented("buffered async handshake is not implemented")
Botan::TLS::Stream::async_shutdown
void async_shutdown(ShutdownHandler &&handler)
Asynchronously shut down SSL on the stream.
Definition: asio_stream.h:381
Botan::TLS::Stream::has_data_to_send
bool has_data_to_send() const
Check if encrypted data is available in the send buffer.
Definition: asio_stream.h:672
Botan::TLS::Stream::m_core
StreamCore m_core
Definition: asio_stream.h:821
Botan::TLS::Stream::BOOST_ASIO_INITFN_RESULT_TYPE
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t)) async_handshake(Connection_Side side
Botan::TLS::TLS_Exception
Definition: tls_exceptn.h:22
Botan::TLS::TLS_Exception::type
Alert::Type type() const
Definition: tls_exceptn.h:24
Botan::TLS::TLS_Exception::error_type
ErrorType error_type() const noexcept override
Definition: tls_exceptn.h:32
Botan::TLS::detail::AsyncHandshakeOperation
Definition: asio_async_ops.h:268
Botan::TLS::detail::AsyncReadOperation
Definition: asio_async_ops.h:112
Botan::TLS::detail::AsyncWriteOperation
Definition: asio_async_ops.h:196
init
int(* init)(CTX *)
Definition: commoncrypto_hash.cpp:27
Botan::TLS::Connection_Side
Connection_Side
Definition: tls_magic.h:32
Botan::TLS::CLIENT
@ CLIENT
Definition: tls_magic.h:32
Botan::TLS::StreamTruncated
@ StreamTruncated
Definition: asio_error.h:35
Botan::TLS::MAX_CIPHERTEXT_SIZE
@ MAX_CIPHERTEXT_SIZE
Definition: tls_magic.h:28
Botan
Definition: alg_id.cpp:13
Botan::ErrorType::Unknown
@ Unknown
Botan::ErrorType::NotImplemented
@ NotImplemented
Botan::Usage_Type
Usage_Type
Definition: x509cert.h:23
std
Definition: bigint.h:1155
type
MechanismType type
Definition: p11_mechanism.cpp:44