-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathtls_context.hpp
More file actions
877 lines (635 loc) · 26.2 KB
/
tls_context.hpp
File metadata and controls
877 lines (635 loc) · 26.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
//
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/corosio
//
#ifndef BOOST_COROSIO_TLS_CONTEXT_HPP
#define BOOST_COROSIO_TLS_CONTEXT_HPP
#include <boost/corosio/detail/config.hpp>
#include <functional>
#include <system_error>
#include <memory>
#include <string_view>
namespace boost::corosio {
//
// Enumerations
//
/** TLS handshake role.
Specifies whether to perform the TLS handshake as a client or server.
@see stream::handshake
*/
enum class tls_role
{
/// Perform handshake as the connecting client.
client,
/// Perform handshake as the accepting server.
server
};
/** TLS protocol version.
Specifies the minimum or maximum TLS protocol version to use
for connections. Only modern, secure versions are supported.
@see tls_context::set_min_protocol_version
@see tls_context::set_max_protocol_version
*/
enum class tls_version
{
/// TLS 1.2 (RFC 5246).
tls_1_2,
/// TLS 1.3 (RFC 8446).
tls_1_3
};
/** Certificate and key file format.
Specifies the encoding format for certificate and key data.
@see tls_context::use_certificate
@see tls_context::use_private_key
*/
enum class tls_file_format
{
/// PEM format (Base64-encoded with header/footer lines).
pem,
/// DER format (raw ASN.1 binary encoding).
der
};
/** Peer certificate verification mode.
Controls how the TLS implementation verifies the peer's
certificate during the handshake.
@see tls_context::set_verify_mode
*/
enum class tls_verify_mode
{
/// Do not request or verify the peer certificate.
none,
/// Request and verify the peer certificate if presented.
peer,
/// Require and verify the peer certificate (fail if not presented).
require_peer
};
/** Certificate revocation checking policy.
Controls how certificate revocation status is checked during
verification.
@see tls_context::set_revocation_policy
*/
enum class tls_revocation_policy
{
/// Do not check revocation status.
disabled,
/// Check revocation but allow connection if status is unknown.
soft_fail,
/// Require successful revocation check (fail if status is unknown).
hard_fail
};
/** Purpose for password callback invocation.
Indicates whether the password is needed for reading (decrypting)
or writing (encrypting) key material.
@see tls_context::set_password_callback
*/
enum class tls_password_purpose
{
/// Password needed to decrypt/read protected key material.
for_reading,
/// Password needed to encrypt/write protected key material.
for_writing
};
class tls_context;
namespace detail {
struct tls_context_data;
tls_context_data const& get_tls_context_data(tls_context const&) noexcept;
} // namespace detail
/** A portable TLS context for certificate and settings storage.
The `tls_context` class provides a backend-agnostic interface for
configuring TLS connections. It stores credentials (certificates and
private keys), trust anchors, protocol settings, and verification
options that are used when establishing TLS connections.
This class is a shared handle to an opaque implementation. Copies
share the same underlying state. This allows contexts to be passed
by value and shared across multiple TLS streams.
This class abstracts the configuration phase of TLS across multiple
backend implementations (OpenSSL, WolfSSL, mbedTLS, Schannel, etc.),
allowing portable code that works regardless of which TLS library
is linked.
@par Modification After Stream Creation
Modifying a context after a TLS stream has been created from it
results in undefined behavior. The context's configuration is
captured when the first stream is constructed, and subsequent
modifications are not reflected in existing or new streams
sharing the context.
If different configurations are needed, create separate context
objects.
@par Thread Safety
Distinct objects: Safe.
Shared objects: Unsafe. A context must not be modified while
any thread is creating streams from it.
@par Example
@code
// Create a client context with system trust anchors
corosio::tls_context ctx;
ctx.set_default_verify_paths();
ctx.set_verify_mode( corosio::tls_verify_mode::peer );
ctx.set_hostname( "example.com" );
// Use with a TLS stream
corosio::openssl_stream secure( sock, ctx );
co_await secure.handshake( corosio::tls_stream::client );
@endcode
@see tls_role
*/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251) // shared_ptr needs dll-interface
#endif
class BOOST_COROSIO_DECL tls_context
{
struct impl;
std::shared_ptr<impl> impl_;
friend detail::tls_context_data const&
detail::get_tls_context_data(tls_context const&) noexcept;
public:
/** Construct a default TLS context.
Creates a context with default settings suitable for TLS 1.2
and TLS 1.3 connections. No certificates or trust anchors are
loaded; call the appropriate methods to configure credentials
and verification.
@par Example
@code
corosio::tls_context ctx;
@endcode
*/
tls_context();
/** Copy constructor.
Creates a new handle that shares ownership of the underlying
TLS context state with `other`.
@param other The context to copy from.
*/
tls_context(tls_context const& other) = default;
/** Copy assignment operator.
Releases the current context's shared ownership and acquires
shared ownership of `other`'s underlying state.
@param other The context to copy from.
@return Reference to this context.
*/
tls_context& operator=(tls_context const& other) = default;
/** Move constructor.
Transfers ownership of the TLS context from another instance.
After the move, `other` is in a valid but empty state.
@param other The context to move from.
*/
tls_context(tls_context&& other) noexcept = default;
/** Move assignment operator.
Releases the current context's shared ownership and transfers
ownership from another instance. After the move, `other` is
in a valid but empty state.
@param other The context to move from.
@return Reference to this context.
*/
tls_context& operator=(tls_context&& other) noexcept = default;
/** Destructor.
Releases this handle's shared ownership of the underlying
context. The context state is destroyed when the last handle
is released.
*/
~tls_context() = default;
//
// Credential Loading
//
/** Load the entity certificate from a memory buffer.
Sets the certificate that identifies this endpoint to the peer.
For servers, this is the server certificate. For clients using
mutual TLS, this is the client certificate.
The certificate must match the private key loaded via
`use_private_key()` or `use_private_key_file()`.
@param certificate The certificate data.
@param format The encoding format of the certificate data.
@return Success, or an error if the certificate could not be parsed
or is invalid.
@see use_certificate_file
@see use_private_key
*/
std::error_code
use_certificate(std::string_view certificate, tls_file_format format);
/** Load the entity certificate from a file.
Sets the certificate that identifies this endpoint to the peer.
For servers, this is the server certificate. For clients using
mutual TLS, this is the client certificate.
@param filename Path to the certificate file.
@param format The encoding format of the file.
@return Success, or an error if the file could not be read or the
certificate is invalid.
@par Example
@code
ctx.use_certificate_file( "server.crt", tls_file_format::pem );
@endcode
@see use_certificate
@see use_private_key_file
*/
std::error_code
use_certificate_file(std::string_view filename, tls_file_format format);
/** Load a certificate chain from a memory buffer.
Loads the entity certificate followed by intermediate CA certificates.
The chain should be ordered from leaf to root (excluding the root).
This is the typical format for PEM certificate bundles.
@param chain The certificate chain data in PEM format (concatenated
certificates).
@return Success, or an error if the chain could not be parsed.
@see use_certificate_chain_file
*/
std::error_code use_certificate_chain(std::string_view chain);
/** Load a certificate chain from a file.
Loads the entity certificate followed by intermediate CA certificates
from a PEM file. The file should contain concatenated PEM certificates
ordered from leaf to root (excluding the root).
@param filename Path to the certificate chain file.
@return Success, or an error if the file could not be read or parsed.
@par Example
@code
// Load certificate chain (cert + intermediates)
ctx.use_certificate_chain_file( "fullchain.pem" );
@endcode
@see use_certificate_chain
*/
std::error_code use_certificate_chain_file(std::string_view filename);
/** Load the private key from a memory buffer.
Sets the private key corresponding to the entity certificate.
The key must match the certificate loaded via `use_certificate()`
or `use_certificate_chain()`.
If the key is encrypted, set a password callback via
`set_password_callback()` before calling this function.
@param private_key The private key data.
@param format The encoding format of the key data.
@return Success, or an error if the key could not be parsed,
is encrypted without a password callback, or doesn't match
the certificate.
@see use_private_key_file
@see set_password_callback
*/
std::error_code
use_private_key(std::string_view private_key, tls_file_format format);
/** Load the private key from a file.
Sets the private key corresponding to the entity certificate.
The key must match the certificate loaded via `use_certificate_file()`
or `use_certificate_chain_file()`.
If the key file is encrypted, set a password callback via
`set_password_callback()` before calling this function.
@param filename Path to the private key file.
@param format The encoding format of the file.
@return Success, or an error if the file could not be read,
the key is invalid, or it doesn't match the certificate.
@par Example
@code
ctx.use_private_key_file( "server.key", tls_file_format::pem );
@endcode
@see use_private_key
@see set_password_callback
*/
std::error_code
use_private_key_file(std::string_view filename, tls_file_format format);
/** Load credentials from a PKCS#12 bundle in memory.
PKCS#12 (also known as PFX) is a binary format that bundles a
certificate, private key, and optionally intermediate certificates
into a single password-protected file.
@param data The PKCS#12 bundle data.
@param passphrase The password protecting the bundle.
@return Success, or an error if the bundle could not be parsed
or the passphrase is incorrect.
@see use_pkcs12_file
*/
std::error_code
use_pkcs12(std::string_view data, std::string_view passphrase);
/** Load credentials from a PKCS#12 file.
PKCS#12 (also known as PFX) is a binary format that bundles a
certificate, private key, and optionally intermediate certificates
into a single password-protected file. This is common on Windows
and for certificates exported from browsers.
@param filename Path to the PKCS#12 file.
@param passphrase The password protecting the file.
@return Success, or an error if the file could not be read,
parsed, or the passphrase is incorrect.
@par Example
@code
ctx.use_pkcs12_file( "credentials.pfx", "secret" );
@endcode
@see use_pkcs12
*/
std::error_code
use_pkcs12_file(std::string_view filename, std::string_view passphrase);
//
// Trust Anchors
//
/** Add a certificate authority for peer verification.
Adds a single CA certificate to the trust store used for verifying
peer certificates. Call this multiple times to add multiple CAs,
or use `load_verify_file()` for a bundle.
@param ca The CA certificate data in PEM format.
@return Success, or an error if the certificate could not be parsed.
@see load_verify_file
@see set_default_verify_paths
*/
std::error_code add_certificate_authority(std::string_view ca);
/** Load CA certificates from a file.
Loads one or more CA certificates from a PEM file. The file may
contain multiple concatenated PEM certificates.
@param filename Path to a PEM file containing CA certificates.
@return Success, or an error if the file could not be read or parsed.
@par Example
@code
// Load a custom CA bundle
ctx.load_verify_file( "/etc/ssl/certs/ca-certificates.crt" );
@endcode
@see add_certificate_authority
@see add_verify_path
*/
std::error_code load_verify_file(std::string_view filename);
/** Add a directory of CA certificates for verification.
Adds a directory containing CA certificate files. Each file must
contain a single certificate in PEM format, named using the
subject name hash (as generated by `openssl rehash` or
`c_rehash`).
@param path Path to the directory containing hashed CA certificates.
@return Success, or an error if the directory is invalid.
@par Example
@code
ctx.add_verify_path( "/etc/ssl/certs" );
@endcode
@see load_verify_file
@see set_default_verify_paths
*/
std::error_code add_verify_path(std::string_view path);
/** Use the system default CA certificate store.
Configures the context to use the operating system's default
trust store for peer certificate verification. This is the
recommended approach for HTTPS clients connecting to public
servers.
On different platforms this uses:
- Linux: `/etc/ssl/certs` or distribution-specific paths
- macOS: System Keychain
- Windows: Windows Certificate Store
@return Success, or an error if the system store could not be loaded.
@par Example
@code
// Trust the same CAs as the system
ctx.set_default_verify_paths();
@endcode
@see load_verify_file
@see add_verify_path
*/
std::error_code set_default_verify_paths();
//
// Protocol Configuration
//
/** Set the minimum TLS protocol version.
Connections will reject protocol versions older than this.
The default allows TLS 1.2 and newer.
@param v The minimum protocol version to accept.
@return Success, or an error if the version is not supported
by the backend.
@par Example
@code
// Require TLS 1.3 minimum
ctx.set_min_protocol_version( tls_version::tls_1_3 );
@endcode
@see set_max_protocol_version
*/
std::error_code set_min_protocol_version(tls_version v);
/** Set the maximum TLS protocol version.
Connections will not negotiate protocol versions newer than this.
The default allows the newest supported version.
@param v The maximum protocol version to accept.
@return Success, or an error if the version is not supported
by the backend.
@see set_min_protocol_version
*/
std::error_code set_max_protocol_version(tls_version v);
/** Set the allowed cipher suites.
Configures which cipher suites may be used for connections.
The format is backend-specific but typically follows OpenSSL
cipher list syntax.
@param ciphers The cipher suite specification string.
@return Success, or an error if the cipher string is invalid.
@par Example
@code
// TLS 1.2 cipher suites (OpenSSL format)
ctx.set_ciphersuites( "ECDHE+AESGCM:ECDHE+CHACHA20" );
@endcode
@note For TLS 1.3, use `set_ciphersuites_tls13()` on backends
that distinguish between TLS 1.2 and 1.3 cipher configuration.
*/
std::error_code set_ciphersuites(std::string_view ciphers);
/** Set the ALPN protocol list.
Configures Application-Layer Protocol Negotiation (ALPN) for
the connection. ALPN is used to negotiate which application
protocol to use over the TLS connection (e.g., "h2" for HTTP/2,
"http/1.1" for HTTP/1.1).
The protocols are tried in preference order (first = highest).
@param protocols Ordered list of protocol identifiers.
@return Success, or an error if ALPN configuration fails.
@par Example
@code
// Prefer HTTP/2, fall back to HTTP/1.1
ctx.set_alpn( { "h2", "http/1.1" } );
@endcode
*/
std::error_code set_alpn(std::initializer_list<std::string_view> protocols);
//
// Certificate Verification
//
/** Set the peer certificate verification mode.
Controls whether and how peer certificates are verified during
the TLS handshake.
@param mode The verification mode to use.
@return Success, or an error if the mode could not be set.
@par Example
@code
// Verify peer certificate (typical for clients)
ctx.set_verify_mode( tls_verify_mode::peer );
// Require client certificate (server-side mTLS)
ctx.set_verify_mode( tls_verify_mode::require_peer );
@endcode
@see tls_verify_mode
*/
std::error_code set_verify_mode(tls_verify_mode mode);
/** Set the maximum certificate chain verification depth.
Limits how many intermediate certificates can appear between
the peer certificate and a trusted root. The default is
typically 100, which is sufficient for most certificate chains.
@param depth Maximum number of intermediate certificates allowed.
@return Success, or an error if the depth is invalid.
*/
std::error_code set_verify_depth(int depth);
/** Set a custom certificate verification callback.
Installs a callback that is invoked during certificate chain
verification. The callback can perform additional validation
beyond the standard checks and can override verification
results.
The callback receives the verification result so far and
information about the certificate being verified. Return
`true` to accept the certificate, `false` to reject.
@tparam Callback A callable with signature
`bool( bool preverified, verify_context& ctx )`.
@param callback The verification callback.
@return Success, or an error if the callback could not be set.
@note The `verify_context` type provides access to the
certificate and chain information. Its exact interface
depends on the TLS backend.
*/
template<typename Callback>
std::error_code set_verify_callback(Callback callback);
/** Set the expected server hostname for verification.
For client connections, sets the hostname that the server
certificate must match. This enables:
1. SNI (Server Name Indication) — tells the server which
certificate to present (for virtual hosting)
2. Hostname verification — validates the certificate's
Subject Alternative Name or Common Name matches
@param hostname The expected server hostname.
@par Example
@code
ctx.set_hostname( "api.example.com" );
@endcode
@note This is typically required for HTTPS clients to ensure
they're connecting to the intended server.
*/
void set_hostname(std::string_view hostname);
/** Set a callback for Server Name Indication (SNI).
For server connections, this callback is invoked during the TLS
handshake when a client sends an SNI extension. The callback
receives the requested hostname and can accept or reject the
connection.
@tparam Callback A callable with signature
`bool( std::string_view hostname )`.
@param callback The SNI callback. Return `true` to accept the
connection or `false` to reject it with an alert.
@par Example
@code
// Accept connections for specific domains only
ctx.set_servername_callback(
[]( std::string_view hostname ) -> bool
{
return hostname == "api.example.com" ||
hostname == "www.example.com";
});
@endcode
@note For virtual hosting with different certificates per hostname,
create separate contexts and select the appropriate one before
creating the TLS stream.
@see set_hostname
*/
template<typename Callback>
void set_servername_callback(Callback callback);
private:
void set_servername_callback_impl(
std::function<bool(std::string_view)> callback);
void set_password_callback_impl(
std::function<std::string(std::size_t, tls_password_purpose)> callback);
public:
//
// Revocation Checking
//
/** Add a Certificate Revocation List from memory.
Adds a CRL to the verification store for checking whether
certificates have been revoked. CRLs are typically fetched
from the URLs in a certificate's CRL Distribution Points
extension.
@param crl The CRL data in DER or PEM format.
@return Success, or an error if the CRL could not be parsed.
@see add_crl_file
@see set_revocation_policy
*/
std::error_code add_crl(std::string_view crl);
/** Add a Certificate Revocation List from a file.
Adds a CRL to the verification store for checking whether
certificates have been revoked.
@param filename Path to a CRL file (DER or PEM format).
@return Success, or an error if the file could not be read
or the CRL is invalid.
@par Example
@code
ctx.add_crl_file( "issuer.crl" );
@endcode
@see add_crl
@see set_revocation_policy
*/
std::error_code add_crl_file(std::string_view filename);
/** Set the OCSP staple response for server-side stapling.
For servers, provides a pre-fetched OCSP response to send
to clients during the handshake. This proves the server's
certificate hasn't been revoked without requiring the client
to contact the OCSP responder.
The OCSP response must be periodically refreshed (typically
every few hours to days) before it expires.
@param response The DER-encoded OCSP response.
@return Success, or an error if the response is invalid.
@note This is a server-side operation. Clients use
`set_require_ocsp_staple()` to require stapled responses.
*/
std::error_code set_ocsp_staple(std::string_view response);
/** Require OCSP stapling from the server.
For clients, requires the server to provide a stapled OCSP
response proving its certificate hasn't been revoked. If
the server doesn't provide a stapled response, the handshake
fails.
@param require Whether to require OCSP stapling.
@note Not all servers support OCSP stapling. Enable this only
when connecting to servers known to support it.
*/
void set_require_ocsp_staple(bool require);
/** Set the certificate revocation checking policy.
Controls how certificate revocation status is checked during
verification. This affects both CRL and OCSP checking.
@param policy The revocation checking policy.
@par Example
@code
// Require successful revocation check
ctx.set_revocation_policy( tls_revocation_policy::hard_fail );
// Check but allow unknown status
ctx.set_revocation_policy( tls_revocation_policy::soft_fail );
@endcode
@see tls_revocation_policy
@see add_crl
*/
void set_revocation_policy(tls_revocation_policy policy);
//
// Password Handling
//
/** Set the password callback for encrypted keys.
Installs a callback that provides passwords for encrypted
private keys and PKCS#12 files. The callback is invoked when
loading encrypted key material.
@tparam Callback A callable with signature
`std::string( std::size_t max_length, password_purpose purpose )`.
@param callback The password callback. It receives the maximum
password length and the purpose (reading or writing), and
returns the password string.
@par Example
@code
ctx.set_password_callback(
[]( std::size_t max_len, tls_password_purpose purpose )
{
// In practice, prompt user or read from secure storage
return std::string( "my-key-password" );
});
// Now load encrypted key
ctx.use_private_key_file( "encrypted.key", tls_file_format::pem );
@endcode
@see tls_password_purpose
*/
template<typename Callback>
void set_password_callback(Callback callback);
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
template<typename Callback>
void
tls_context::set_servername_callback(Callback callback)
{
set_servername_callback_impl(std::move(callback));
}
template<typename Callback>
void
tls_context::set_password_callback(Callback callback)
{
set_password_callback_impl(std::move(callback));
}
} // namespace boost::corosio
#endif