Botan 3.7.1
Crypto and TLS for C&
tls_policy.cpp
Go to the documentation of this file.
1/*
2* Policies for TLS
3* (C) 2004-2010,2012,2015,2016 Jack Lloyd
4* 2016 Christian Mainka
5* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
6* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH
7*
8* Botan is released under the Simplified BSD License (see license.txt)
9*/
10
11#include <botan/tls_policy.h>
12
13#include <botan/pk_keys.h>
14#include <botan/tls_algos.h>
15#include <botan/tls_ciphersuite.h>
16#include <botan/tls_exceptn.h>
17#include <botan/internal/stl_util.h>
18#include <optional>
19#include <sstream>
20
21namespace Botan::TLS {
22
24 return false;
25}
26
27std::vector<Signature_Scheme> Policy::allowed_signature_schemes() const {
28 std::vector<Signature_Scheme> schemes;
29
31 const bool sig_allowed = allowed_signature_method(scheme.algorithm_name());
32 const bool hash_allowed = allowed_signature_hash(scheme.hash_function_name());
33
34 if(sig_allowed && hash_allowed) {
35 schemes.push_back(scheme);
36 }
37 }
38
39 return schemes;
40}
41
42std::vector<Signature_Scheme> Policy::acceptable_signature_schemes() const {
43 return this->allowed_signature_schemes();
44}
45
46std::optional<std::vector<Signature_Scheme>> Policy::acceptable_certificate_signature_schemes() const {
47 // the restrictions of ::acceptable_signature_schemes() shall apply
48 return std::nullopt;
49}
50
51std::vector<std::string> Policy::allowed_ciphers() const {
52 return {
53 //"AES-256/OCB(12)",
54 "ChaCha20Poly1305",
55 "AES-256/GCM",
56 "AES-128/GCM",
57 //"AES-256/CCM",
58 //"AES-128/CCM",
59 //"AES-256/CCM(8)",
60 //"AES-128/CCM(8)",
61 //"Camellia-256/GCM",
62 //"Camellia-128/GCM",
63 //"ARIA-256/GCM",
64 //"ARIA-128/GCM",
65 //"AES-256",
66 //"AES-128",
67 //"3DES",
68 };
69}
70
71std::vector<std::string> Policy::allowed_signature_hashes() const {
72 return {
73 "SHA-512",
74 "SHA-384",
75 "SHA-256",
76 };
77}
78
79std::vector<std::string> Policy::allowed_macs() const {
80 /*
81 SHA-256 is preferred because the Lucky13 countermeasure works
82 somewhat better for SHA-256 vs SHA-384:
83 https://github.com/randombit/botan/pull/675
84 */
85 return {
86 "AEAD",
87 "SHA-256",
88 "SHA-384",
89 "SHA-1",
90 };
91}
92
93std::vector<std::string> Policy::allowed_key_exchange_methods() const {
94 return {
95 //"ECDHE_PSK",
96 //"PSK",
97 "ECDH",
98 "DH",
99 //"RSA",
100 };
101}
102
103std::vector<std::string> Policy::allowed_signature_methods() const {
104 return {
105 "ECDSA", "RSA",
106 //"IMPLICIT",
107 };
108}
109
110bool Policy::allowed_signature_method(std::string_view sig_method) const {
111 return value_exists(allowed_signature_methods(), sig_method);
112}
113
114bool Policy::allowed_signature_hash(std::string_view sig_hash) const {
115 return value_exists(allowed_signature_hashes(), sig_hash);
116}
117
119 return false;
120}
121
122Group_Params Policy::choose_key_exchange_group(const std::vector<Group_Params>& supported_by_peer,
123 const std::vector<Group_Params>& offered_by_peer) const {
124 if(supported_by_peer.empty()) {
125 return Group_Params::NONE;
126 }
127
128 const auto our_groups = key_exchange_groups();
129
130 // First check if the peer sent a PQ share of a group we also support
131 for(auto share : offered_by_peer) {
132 if(share.is_post_quantum() && value_exists(our_groups, share)) {
133 return share;
134 }
135 }
136
137 // Then check if the peer offered a PQ algo we also support
138 for(auto share : supported_by_peer) {
139 if(share.is_post_quantum() && value_exists(our_groups, share)) {
140 return share;
141 }
142 }
143
144 // Prefer groups that were offered by the peer for the sake of saving
145 // an additional round trip. For TLS 1.2, this won't be used.
146 for(auto g : offered_by_peer) {
147 if(value_exists(our_groups, g)) {
148 return g;
149 }
150 }
151
152 // If no pre-offered groups fit our supported set, we prioritize our
153 // own preference.
154 for(auto g : our_groups) {
155 if(value_exists(supported_by_peer, g)) {
156 return g;
157 }
158 }
159
160 return Group_Params::NONE;
161}
162
164 /*
165 * Return the first listed or just default to 2048
166 */
167 for(auto g : key_exchange_groups()) {
168 if(g.is_dh_named_group()) {
169 return g;
170 }
171 }
172
173 return Group_Params::FFDHE_2048;
174}
175
176std::vector<Group_Params> Policy::key_exchange_groups() const {
177 return {
178 // clang-format off
179#if defined(BOTAN_HAS_X25519)
180 Group_Params::X25519,
181#endif
182
183 Group_Params::SECP256R1,
184
185#if defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_ML_KEM) && defined(BOTAN_HAS_TLS_13_PQC)
187#endif
188
189#if defined(BOTAN_HAS_X448)
190 Group_Params::X448,
191#endif
192
193 Group_Params::SECP384R1,
194 Group_Params::SECP521R1,
195
196 Group_Params::BRAINPOOL256R1,
197 Group_Params::BRAINPOOL384R1,
198 Group_Params::BRAINPOOL512R1,
199
200 Group_Params::FFDHE_2048,
201 Group_Params::FFDHE_3072,
202
203 // clang-format on
204 };
205}
206
207std::vector<Group_Params> Policy::key_exchange_groups_to_offer() const {
208 std::vector<Group_Params> groups_to_offer;
209
210 const auto supported_groups = key_exchange_groups();
211 BOTAN_ASSERT(!supported_groups.empty(), "Policy allows at least one key exchange group");
212
213 /*
214 * Initially prefer sending a key share only of the first pure-ECC
215 * group, since these shares are small and PQ support is still not
216 * that widespread.
217 */
218 for(auto group : key_exchange_groups()) {
219 if(group.is_pure_ecc_group()) {
220 groups_to_offer.push_back(group);
221 break;
222 }
223 }
224
225 /*
226 * If for some reason no pure ECC groups are enabled then simply
227 * send a share of whatever the policys top preference is.
228 */
229 if(groups_to_offer.empty()) {
230 groups_to_offer.push_back(supported_groups.front());
231 }
232
233 return groups_to_offer;
234}
235
237 return 2048;
238}
239
241 // Here we are at the mercy of whatever the CA signed, but most certs should be 256 bit by now
242 return 256;
243}
244
246 // x25519 is smallest curve currently supported for TLS key exchange
247 return 255;
248}
249
251 return 110;
252}
253
255 return true;
256}
257
259 /* Default assumption is all end-entity certificates should
260 be at least 2048 bits these days.
261
262 If you are connecting to arbitrary servers on the Internet
263 (ie as a web browser or SMTP client) you'll probably have to reduce this
264 to 1024 bits, or perhaps even lower.
265 */
266 return 2048;
267}
268
269void Policy::check_peer_key_acceptable(const Public_Key& public_key) const {
270 const std::string algo_name = public_key.algo_name();
271
272 const size_t keylength = public_key.key_length();
273 size_t expected_keylength = 0;
274
275 if(algo_name == "RSA") {
276 expected_keylength = minimum_rsa_bits();
277 } else if(algo_name == "DH") {
278 expected_keylength = minimum_dh_group_size();
279 } else if(algo_name == "ECDH" || algo_name == "X25519" || algo_name == "X448") {
280 expected_keylength = minimum_ecdh_group_size();
281 } else if(algo_name == "ECDSA") {
282 expected_keylength = minimum_ecdsa_group_size();
283 }
284 // else some other algo, so leave expected_keylength as zero and the check is a no-op
285
286 if(keylength < expected_keylength) {
287 throw TLS_Exception(Alert::InsufficientSecurity,
288 "Peer sent " + std::to_string(keylength) + " bit " + algo_name +
289 " key"
290 ", policy requires at least " +
291 std::to_string(expected_keylength));
292 }
293}
294
296 return 1;
297}
298
299std::chrono::seconds Policy::session_ticket_lifetime() const {
300 return std::chrono::days(1);
301}
302
304 return false;
305}
306
308 return 1;
309}
310
312#if defined(BOTAN_HAS_TLS_13)
313 if(version == Protocol_Version::TLS_V13 && allow_tls13()) {
314 return true;
315 }
316#endif
317
318#if defined(BOTAN_HAS_TLS_12)
319 if(version == Protocol_Version::TLS_V12 && allow_tls12()) {
320 return true;
321 }
322
323 if(version == Protocol_Version::DTLS_V12 && allow_dtls12()) {
324 return true;
325 }
326#endif
327
328 return false;
329}
330
332 if(datagram) {
333 if(acceptable_protocol_version(Protocol_Version::DTLS_V12)) {
334 return Protocol_Version::DTLS_V12;
335 }
336 throw Invalid_State("Policy forbids all available DTLS version");
337 } else {
338#if defined(BOTAN_HAS_TLS_13)
339 if(acceptable_protocol_version(Protocol_Version::TLS_V13)) {
340 return Protocol_Version::TLS_V13;
341 }
342#endif
343 if(acceptable_protocol_version(Protocol_Version::TLS_V12)) {
344 return Protocol_Version::TLS_V12;
345 }
346 throw Invalid_State("Policy forbids all available TLS version");
347 }
348}
349
350bool Policy::acceptable_ciphersuite(const Ciphersuite& ciphersuite) const {
351 return value_exists(allowed_ciphers(), ciphersuite.cipher_algo()) &&
352 value_exists(allowed_macs(), ciphersuite.mac_algo());
353}
354
356 return false;
357}
358
360 return false;
361}
362
364 return false;
365}
366
368#if defined(BOTAN_HAS_TLS_12)
369 return true;
370#else
371 return false;
372#endif
373}
374
376#if defined(BOTAN_HAS_TLS_13)
377 return true;
378#else
379 return false;
380#endif
381}
382
384#if defined(BOTAN_HAS_TLS_12)
385 return true;
386#else
387 return false;
388#endif
389}
390
392 return true;
393}
394
396 return false;
397}
398
400 return true;
401}
402
404 return true;
405}
406
407std::optional<uint16_t> Policy::record_size_limit() const {
408 return std::nullopt;
409}
410
412 return true;
413}
414
416 return true;
417}
418
420 return true;
421}
422
424 return true;
425}
426
428 return true;
429}
430
432 return false;
433}
434
438
440 return false;
441}
442
443std::vector<Certificate_Type> Policy::accepted_client_certificate_types() const {
444 return {Certificate_Type::X509};
445}
446
447std::vector<Certificate_Type> Policy::accepted_server_certificate_types() const {
448 return {Certificate_Type::X509};
449}
450
452 return false;
453}
454
456 return 0;
457}
458
459// 1 second initial timeout, 60 second max - see RFC 6347 sec 4.2.4.1
461 return 1 * 1000;
462}
463
465 return 60 * 1000;
466}
467
469 // default MTU is IPv6 min MTU minus UDP/IP headers
470 return 1280 - 40 - 8;
471}
472
473std::vector<uint16_t> Policy::srtp_profiles() const {
474 return std::vector<uint16_t>();
475}
476
477namespace {
478
479class Ciphersuite_Preference_Ordering final {
480 public:
481 Ciphersuite_Preference_Ordering(const std::vector<std::string>& ciphers,
482 const std::vector<std::string>& macs,
483 const std::vector<std::string>& kex,
484 const std::vector<std::string>& sigs) :
485 m_ciphers(ciphers), m_macs(macs), m_kex(kex), m_sigs(sigs) {}
486
487 bool operator()(const Ciphersuite& a, const Ciphersuite& b) const {
488 if(a.kex_method() != b.kex_method()) {
489 for(const auto& i : m_kex) {
490 if(a.kex_algo() == i) {
491 return true;
492 }
493 if(b.kex_algo() == i) {
494 return false;
495 }
496 }
497 }
498
499 if(a.cipher_algo() != b.cipher_algo()) {
500 for(const auto& m_cipher : m_ciphers) {
501 if(a.cipher_algo() == m_cipher) {
502 return true;
503 }
504 if(b.cipher_algo() == m_cipher) {
505 return false;
506 }
507 }
508 }
509
510 if(a.cipher_keylen() != b.cipher_keylen()) {
511 if(a.cipher_keylen() < b.cipher_keylen()) {
512 return false;
513 }
514 if(a.cipher_keylen() > b.cipher_keylen()) {
515 return true;
516 }
517 }
518
519 if(a.auth_method() != b.auth_method()) {
520 for(const auto& m_sig : m_sigs) {
521 if(a.sig_algo() == m_sig) {
522 return true;
523 }
524 if(b.sig_algo() == m_sig) {
525 return false;
526 }
527 }
528 }
529
530 if(a.mac_algo() != b.mac_algo()) {
531 for(const auto& m_mac : m_macs) {
532 if(a.mac_algo() == m_mac) {
533 return true;
534 }
535 if(b.mac_algo() == m_mac) {
536 return false;
537 }
538 }
539 }
540
541 return false; // equal (?!?)
542 }
543
544 private:
545 std::vector<std::string> m_ciphers, m_macs, m_kex, m_sigs;
546};
547
548} // namespace
549
550std::vector<uint16_t> Policy::ciphersuite_list(Protocol_Version version) const {
551 const std::vector<std::string> ciphers = allowed_ciphers();
552 const std::vector<std::string> macs = allowed_macs();
553 const std::vector<std::string> kex = allowed_key_exchange_methods();
554 const std::vector<std::string> sigs = allowed_signature_methods();
555
556 std::vector<Ciphersuite> ciphersuites;
557
558 for(auto&& suite : Ciphersuite::all_known_ciphersuites()) {
559 // Can we use it?
560 if(!suite.valid()) {
561 continue;
562 }
563
564 // Can we use it in this version?
565 if(!suite.usable_in_version(version)) {
566 continue;
567 }
568
569 // Is it acceptable to the policy?
570 if(!this->acceptable_ciphersuite(suite)) {
571 continue;
572 }
573
574 if(!value_exists(ciphers, suite.cipher_algo())) {
575 continue; // unsupported cipher
576 }
577
578 // these checks are irrelevant for TLS 1.3
579 // TODO: consider making a method for this logic
580 if(version.is_pre_tls_13()) {
581 if(!value_exists(kex, suite.kex_algo())) {
582 continue; // unsupported key exchange
583 }
584
585 if(!value_exists(macs, suite.mac_algo())) {
586 continue; // unsupported MAC algo
587 }
588
589 if(!value_exists(sigs, suite.sig_algo())) {
590 // allow if it's an empty sig algo and we want to use PSK
591 if(suite.auth_method() != Auth_Method::IMPLICIT || !suite.psk_ciphersuite()) {
592 continue;
593 }
594 }
595 }
596
597 // OK, consider it
598 ciphersuites.push_back(suite);
599 }
600
601 if(ciphersuites.empty()) {
602 throw Invalid_State("Policy does not allow any available cipher suite");
603 }
604
605 Ciphersuite_Preference_Ordering order(ciphers, macs, kex, sigs);
606 std::sort(ciphersuites.begin(), ciphersuites.end(), order);
607
608 std::vector<uint16_t> ciphersuite_codes;
609 ciphersuite_codes.reserve(ciphersuites.size());
610 for(auto i : ciphersuites) {
611 ciphersuite_codes.push_back(i.ciphersuite_code());
612 }
613 return ciphersuite_codes;
614}
615
616namespace {
617
618void print_vec(std::ostream& o, const char* key, const std::vector<std::string>& v) {
619 o << key << " = ";
620 for(size_t i = 0; i != v.size(); ++i) {
621 o << v[i];
622 if(i != v.size() - 1) {
623 o << ' ';
624 }
625 }
626 o << '\n';
627}
628
629void print_vec(std::ostream& o, const char* key, const std::vector<Group_Params>& params) {
630 // first filter out any groups we don't have a name for:
631 std::vector<std::string> names;
632 for(auto p : params) {
633 if(auto name = p.to_string()) {
634 names.push_back(name.value());
635 }
636 }
637
638 o << key << " = ";
639
640 for(size_t i = 0; i != names.size(); ++i) {
641 o << names[i];
642 if(i != names.size() - 1) {
643 o << " ";
644 }
645 }
646 o << "\n";
647}
648
649void print_vec(std::ostream& o, const char* key, const std::vector<Certificate_Type>& types) {
650 o << key << " = ";
651 for(size_t i = 0; i != types.size(); ++i) {
652 o << certificate_type_to_string(types[i]);
653 if(i != types.size() - 1) {
654 o << ' ';
655 }
656 }
657 o << '\n';
658}
659
660void print_bool(std::ostream& o, const char* key, bool b) {
661 o << key << " = " << (b ? "true" : "false") << '\n';
662}
663
664} // namespace
665
666void Policy::print(std::ostream& o) const {
667 print_bool(o, "allow_tls12", allow_tls12());
668 print_bool(o, "allow_tls13", allow_tls13());
669 print_bool(o, "allow_dtls12", allow_dtls12());
670 print_bool(o, "allow_ssl_key_log_file", allow_ssl_key_log_file());
671 print_vec(o, "ciphers", allowed_ciphers());
672 print_vec(o, "macs", allowed_macs());
673 print_vec(o, "signature_hashes", allowed_signature_hashes());
674 print_vec(o, "signature_methods", allowed_signature_methods());
675 print_vec(o, "key_exchange_methods", allowed_key_exchange_methods());
676 print_vec(o, "key_exchange_groups", key_exchange_groups());
677 const auto groups_to_offer = key_exchange_groups_to_offer();
678 if(groups_to_offer.empty()) {
679 print_vec(o, "key_exchange_groups_to_offer", {std::string("none")});
680 } else {
681 print_vec(o, "key_exchange_groups_to_offer", groups_to_offer);
682 }
683 print_bool(o, "allow_insecure_renegotiation", allow_insecure_renegotiation());
684 print_bool(o, "include_time_in_hello_random", include_time_in_hello_random());
685 print_bool(o, "allow_server_initiated_renegotiation", allow_server_initiated_renegotiation());
686 print_bool(o, "hide_unknown_users", hide_unknown_users());
687 print_bool(o, "server_uses_own_ciphersuite_preferences", server_uses_own_ciphersuite_preferences());
688 print_bool(o, "negotiate_encrypt_then_mac", negotiate_encrypt_then_mac());
689 print_bool(o, "support_cert_status_message", support_cert_status_message());
690 print_bool(o, "tls_13_middlebox_compatibility_mode", tls_13_middlebox_compatibility_mode());
691 print_vec(o, "accepted_client_certificate_types", accepted_client_certificate_types());
692 print_vec(o, "accepted_server_certificate_types", accepted_server_certificate_types());
693 print_bool(o, "hash_hello_random", hash_hello_random());
694 if(record_size_limit().has_value()) {
695 o << "record_size_limit = " << record_size_limit().value() << '\n';
696 }
697 o << "maximum_session_tickets_per_client_hello = " << maximum_session_tickets_per_client_hello() << '\n';
698 o << "session_ticket_lifetime = " << session_ticket_lifetime().count() << '\n';
699 print_bool(o, "reuse_session_tickets", reuse_session_tickets());
700 o << "new_session_tickets_upon_handshake_success = " << new_session_tickets_upon_handshake_success() << '\n';
701 o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n';
702 o << "minimum_ecdh_group_size = " << minimum_ecdh_group_size() << '\n';
703 o << "minimum_rsa_bits = " << minimum_rsa_bits() << '\n';
704 o << "minimum_signature_strength = " << minimum_signature_strength() << '\n';
705}
706
707std::string Policy::to_string() const {
708 std::ostringstream oss;
709 this->print(oss);
710 return oss.str();
711}
712
713std::vector<std::string> Strict_Policy::allowed_ciphers() const {
714 return {"ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM"};
715}
716
717std::vector<std::string> Strict_Policy::allowed_signature_hashes() const {
718 return {"SHA-512", "SHA-384"};
719}
720
721std::vector<std::string> Strict_Policy::allowed_macs() const {
722 return {"AEAD"};
723}
724
725std::vector<std::string> Strict_Policy::allowed_key_exchange_methods() const {
726 return {"ECDH"};
727}
728
729} // namespace Botan::TLS
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
virtual std::string algo_name() const =0
virtual size_t key_length() const =0
static const std::vector< Ciphersuite > & all_known_ciphersuites()
std::string mac_algo() const
std::string cipher_algo() const
virtual bool include_time_in_hello_random() const
virtual void check_peer_key_acceptable(const Public_Key &public_key) const
virtual bool abort_connection_on_undesired_renegotiation() const
virtual size_t dtls_maximum_timeout() const
virtual size_t minimum_ecdh_group_size() const
virtual size_t dtls_default_mtu() const
virtual bool allow_tls12() const
virtual std::vector< Signature_Scheme > allowed_signature_schemes() const
std::string to_string() const
virtual bool reuse_session_tickets() const
virtual std::vector< uint16_t > ciphersuite_list(Protocol_Version version) const
virtual std::vector< Certificate_Type > accepted_server_certificate_types() const
virtual std::vector< Certificate_Type > accepted_client_certificate_types() const
bool allowed_signature_method(std::string_view sig_method) const
virtual bool require_client_certificate_authentication() const
virtual std::vector< Group_Params > key_exchange_groups() const
virtual size_t new_session_tickets_upon_handshake_success() const
virtual std::vector< Group_Params > key_exchange_groups_to_offer() const
bool allowed_signature_hash(std::string_view hash) const
virtual size_t minimum_rsa_bits() const
virtual bool tls_13_middlebox_compatibility_mode() const
virtual bool only_resume_with_exact_version() const
virtual bool allow_client_initiated_renegotiation() const
virtual bool allow_ssl_key_log_file() const
virtual bool allow_dtls_epoch0_restart() const
virtual bool request_client_certificate_authentication() const
virtual bool require_cert_revocation_info() const
virtual bool negotiate_encrypt_then_mac() const
virtual bool server_uses_own_ciphersuite_preferences() const
virtual Protocol_Version latest_supported_version(bool datagram) const
virtual bool acceptable_protocol_version(Protocol_Version version) const
virtual std::vector< uint16_t > srtp_profiles() const
virtual bool support_cert_status_message() const
virtual bool acceptable_ciphersuite(const Ciphersuite &suite) const
virtual std::vector< std::string > allowed_macs() const
virtual bool hide_unknown_users() const
virtual std::optional< std::vector< Signature_Scheme > > acceptable_certificate_signature_schemes() const
virtual bool hash_hello_random() const
virtual bool allow_tls13() const
virtual std::vector< std::string > allowed_key_exchange_methods() const
virtual size_t dtls_initial_timeout() const
virtual size_t maximum_session_tickets_per_client_hello() const
virtual std::vector< Signature_Scheme > acceptable_signature_schemes() const
virtual bool use_ecc_point_compression() const
virtual bool allow_dtls12() const
virtual size_t minimum_dh_group_size() const
virtual bool allow_insecure_renegotiation() const
virtual std::optional< uint16_t > record_size_limit() const
virtual std::vector< std::string > allowed_ciphers() const
virtual std::chrono::seconds session_ticket_lifetime() const
virtual size_t minimum_signature_strength() const
virtual Group_Params default_dh_group() const
virtual size_t maximum_certificate_chain_size() const
virtual std::vector< std::string > allowed_signature_methods() const
virtual size_t minimum_ecdsa_group_size() const
virtual Group_Params choose_key_exchange_group(const std::vector< Group_Params > &supported_by_peer, const std::vector< Group_Params > &offered_by_peer) const
virtual bool allow_resumption_for_renegotiation() const
virtual std::vector< std::string > allowed_signature_hashes() const
virtual bool allow_server_initiated_renegotiation() const
virtual void print(std::ostream &o) const
static const std::vector< Signature_Scheme > & all_available_schemes()
std::vector< std::string > allowed_macs() const override
std::vector< std::string > allowed_ciphers() const override
std::vector< std::string > allowed_key_exchange_methods() const override
std::vector< std::string > allowed_signature_hashes() const override
std::string name
int(* final)(unsigned char *, CTX *)
std::string certificate_type_to_string(Certificate_Type type)
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:60
const SIMD_8x32 & b