• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 13262741994

11 Feb 2025 12:19PM UTC coverage: 91.656% (-0.003%) from 91.659%
13262741994

Pull #4647

github

web-flow
Merge 0b8e56724 into f372b5a9e
Pull Request #4647: Avoid using mem_ops.h or assert.h in public headers

94864 of 103500 relevant lines covered (91.66%)

11330304.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.31
/src/cli/tls_helpers.h
1
/*
2
* (C) 2014,2015,2019 Jack Lloyd
3
*     2023           René Meusel, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#ifndef BOTAN_CLI_TLS_HELPERS_H_
9
#define BOTAN_CLI_TLS_HELPERS_H_
10

11
#include <botan/assert.h>
12
#include <botan/credentials_manager.h>
13
#include <botan/data_src.h>
14
#include <botan/hex.h>
15
#include <botan/pkcs8.h>
16
#include <botan/tls_policy.h>
17
#include <botan/x509_key.h>
18
#include <botan/x509self.h>
19
#include <fstream>
20
#include <memory>
21
#include <utility>
22

23
#include "cli_exceptions.h"
24

25
#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
26
   #include <botan/certstor_system.h>
27
#endif
28

29
inline bool value_exists(const std::vector<std::string>& vec, const std::string& val) {
×
30
   for(size_t i = 0; i != vec.size(); ++i) {
×
31
      if(vec[i] == val) {
×
32
         return true;
33
      }
34
   }
35
   return false;
36
}
37

38
inline std::string maybe_hex_encode(std::string_view v) {
4✔
39
   auto is_printable_char = [](uint8_t c) { return c >= 32 && c < 127; };
8✔
40
   if(!std::all_of(v.begin(), v.end(), is_printable_char)) {
4✔
41
      return Botan::hex_encode(std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size()));
×
42
   }
43
   return std::string(v);
4✔
44
}
45

46
class Basic_Credentials_Manager : public Botan::Credentials_Manager {
47
   protected:
48
      void load_credentials(const std::string& cred, const std::string& key) {
47✔
49
         Botan::DataSource_Stream key_in(key);
47✔
50
         auto privkey = Botan::PKCS8::load_key(key_in);
47✔
51

52
         // first try to read @p cred as a public key
53
         try {
47✔
54
            auto pubkey = Botan::X509::load_key(cred);
47✔
55
            m_raw_pubkey = {std::exchange(privkey, {}), std::move(pubkey)};
×
56
            return;
×
57
         } catch(const Botan::Decoding_Error&) {}
47✔
58

59
         // ... then try again assuming that @p cred contains a certificate chain
60
         BOTAN_ASSERT_NONNULL(privkey);
47✔
61
         Certificate_Info cert;
47✔
62
         cert.key = std::move(privkey);
47✔
63

64
         Botan::DataSource_Stream in(cred);
47✔
65
         while(!in.end_of_data()) {
141✔
66
            try {
94✔
67
               cert.certs.push_back(Botan::X509_Certificate(in));
141✔
68
            } catch(std::exception&) {}
47✔
69
         }
70

71
         BOTAN_ARG_CHECK(!cert.certs.empty(), "Provided certificate chain file cannot be loaded");
47✔
72

73
         // TODO: attempt to validate chain ourselves
74

75
         m_certs.push_back(cert);
47✔
76
      }
47✔
77

78
   public:
79
      Basic_Credentials_Manager(bool use_system_store,
54✔
80
                                const std::string& ca_path,
81
                                std::optional<std::string> client_cred = std::nullopt,
82
                                std::optional<std::string> client_key = std::nullopt,
83
                                std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
84
                                std::optional<std::string> psk_identity = std::nullopt,
85
                                const std::optional<std::string>& psk_prf = std::nullopt) :
54✔
86
            m_psk(std::move(psk)),
54✔
87
            m_psk_identity(std::move(psk_identity)),
54✔
88

89
            // RFC 8446 4.2.11
90
            //    the Hash algorithm MUST be set when the PSK is established or
91
            //    default to SHA-256 if no such algorithm is defined.
92
            m_psk_prf(psk_prf.value_or("SHA-256")) {
108✔
93
         if(ca_path.empty() == false) {
54✔
94
            m_certstores.push_back(std::make_shared<Botan::Certificate_Store_In_Memory>(ca_path));
10✔
95
         }
96

97
         BOTAN_ARG_CHECK(client_cred.has_value() == client_key.has_value(),
54✔
98
                         "either provide both client certificate and key or neither");
99

100
         if(client_cred.has_value() && client_key.has_value()) {
54✔
101
            load_credentials(client_cred.value(), client_key.value());
×
102
         }
103

104
#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
105
         if(use_system_store) {
54✔
106
            m_certstores.push_back(std::make_shared<Botan::System_Certificate_Store>());
54✔
107
         }
108
#else
109
         BOTAN_UNUSED(use_system_store);
110
#endif
111
      }
54✔
112

113
      Basic_Credentials_Manager(const std::string& server_cred,
47✔
114
                                const std::string& server_key,
115
                                std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
116
                                std::optional<std::string> psk_identity = std::nullopt,
117
                                const std::optional<std::string>& psk_prf = std::nullopt) :
47✔
118
            m_psk(std::move(psk)),
47✔
119
            m_psk_identity(std::move(psk_identity)),
47✔
120

121
            // RFC 8446 4.2.11
122
            //    the Hash algorithm MUST be set when the PSK is established or
123
            //    default to SHA-256 if no such algorithm is defined.
124
            m_psk_prf(psk_prf.value_or("SHA-256")) {
94✔
125
         load_credentials(server_cred, server_key);
47✔
126
      }
47✔
127

128
      std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& type,
96✔
129
                                                                             const std::string& /*hostname*/) override {
130
         std::vector<Botan::Certificate_Store*> v;
96✔
131

132
         // don't ask for client certs
133
         if(type == "tls-server") {
96✔
134
            return v;
135
         }
136

137
         for(const auto& cs : m_certstores) {
93✔
138
            v.push_back(cs.get());
50✔
139
         }
140

141
         return v;
142
      }
×
143

144
      std::vector<Botan::X509_Certificate> find_cert_chain(
119✔
145
         const std::vector<std::string>& algos,
146
         const std::vector<Botan::AlgorithmIdentifier>& cert_signature_schemes,
147
         const std::vector<Botan::X509_DN>& acceptable_cas,
148
         const std::string& type,
149
         const std::string& hostname) override {
150
         BOTAN_UNUSED(cert_signature_schemes);
119✔
151

152
         if(type == "tls-client") {
119✔
153
            for(const auto& dn : acceptable_cas) {
×
154
               for(const auto& cred : m_certs) {
×
155
                  if(dn == cred.certs[0].issuer_dn()) {
×
156
                     return cred.certs;
×
157
                  }
158
               }
159
            }
160
         } else if(type == "tls-server") {
119✔
161
            for(const auto& i : m_certs) {
186✔
162
               if(std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end()) {
119✔
163
                  continue;
67✔
164
               }
165

166
               if(!hostname.empty() && !i.certs[0].matches_dns_name(hostname)) {
52✔
167
                  continue;
×
168
               }
169

170
               return i.certs;
52✔
171
            }
172
         }
173

174
         return {};
67✔
175
      }
176

177
      std::shared_ptr<Botan::Public_Key> find_raw_public_key(const std::vector<std::string>& algos,
×
178
                                                             const std::string& type,
179
                                                             const std::string& hostname) override {
180
         BOTAN_UNUSED(type, hostname);
×
181
         return (algos.empty() || value_exists(algos, m_raw_pubkey.public_key->algo_name())) ? m_raw_pubkey.public_key
×
182
                                                                                             : nullptr;
×
183
      }
184

185
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& cert,
51✔
186
                                                          const std::string& /*type*/,
187
                                                          const std::string& /*context*/) override {
188
         for(const auto& i : m_certs) {
51✔
189
            if(cert == i.certs[0]) {
51✔
190
               return i.key;
51✔
191
            }
192
         }
193

194
         return nullptr;
×
195
      }
196

197
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::Public_Key& raw_public_key,
×
198
                                                          const std::string& /*type*/,
199
                                                          const std::string& /*context*/) override {
200
         return (m_raw_pubkey.public_key->fingerprint_public() == raw_public_key.fingerprint_public())
×
201
                   ? m_raw_pubkey.private_key
×
202
                   : nullptr;
×
203
      }
204

205
      std::string psk_identity(const std::string& type,
1✔
206
                               const std::string& context,
207
                               const std::string& identity_hint) override {
208
         return (m_psk_identity && (type == "tls-client" || type == "tls-server"))
1✔
209
                   ? m_psk_identity.value()
2✔
210
                   : Botan::Credentials_Manager::psk_identity(type, context, identity_hint);
1✔
211
      }
212

213
      std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
32✔
214
         std::string_view host,
215
         Botan::TLS::Connection_Side peer_type,
216
         const std::vector<std::string>& ids = {},
217
         const std::optional<std::string>& prf = std::nullopt) override {
218
         if(!m_psk.has_value() || !m_psk_identity.has_value()) {
32✔
219
            return Botan::Credentials_Manager::find_preshared_keys(host, peer_type, ids, prf);
28✔
220
         }
221

222
         std::vector<Botan::TLS::ExternalPSK> psks;
4✔
223

224
         const bool prf_matches = !prf.has_value() || m_psk_prf == prf.value();
4✔
225
         const bool id_matches = ids.empty() || std::find(ids.begin(), ids.end(), m_psk_identity.value()) != ids.end();
4✔
226

227
         if(prf_matches && id_matches) {
4✔
228
            psks.emplace_back(m_psk_identity.value(), m_psk_prf, m_psk.value());
4✔
229
         }
230

231
         return psks;
4✔
232
      }
4✔
233

234
   private:
235
      struct Certificate_Info {
47✔
236
            std::vector<Botan::X509_Certificate> certs;
237
            std::shared_ptr<Botan::Private_Key> key;
238
      };
239

240
      struct RawPublicKey_Info {
101✔
241
            std::shared_ptr<Botan::Private_Key> private_key;
242
            std::shared_ptr<Botan::Public_Key> public_key;
243
      };
244

245
      std::vector<Certificate_Info> m_certs;
246
      RawPublicKey_Info m_raw_pubkey;
247

248
      std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
249
      std::optional<Botan::secure_vector<uint8_t>> m_psk;
250
      std::optional<std::string> m_psk_identity;
251
      std::string m_psk_prf;
252
};
253

254
class TLS_All_Policy final : public Botan::TLS::Policy {
2✔
255
   public:
256
      std::vector<std::string> allowed_ciphers() const override {
90✔
257
         return std::vector<std::string>{"ChaCha20Poly1305",
90✔
258
                                         "AES-256/OCB(12)",
259
                                         "AES-128/OCB(12)",
260
                                         "AES-256/GCM",
261
                                         "AES-128/GCM",
262
                                         "AES-256/CCM",
263
                                         "AES-128/CCM",
264
                                         "AES-256/CCM(8)",
265
                                         "AES-128/CCM(8)",
266
                                         "Camellia-256/GCM",
267
                                         "Camellia-128/GCM",
268
                                         "ARIA-256/GCM",
269
                                         "ARIA-128/GCM",
270
                                         "AES-256",
271
                                         "AES-128",
272
                                         "Camellia-256",
273
                                         "Camellia-128",
274
                                         "SEED",
275
                                         "3DES"};
1,890✔
276
      }
180✔
277

278
      std::vector<std::string> allowed_key_exchange_methods() const override {
1✔
279
         return {"ECDHE_PSK", "DHE_PSK", "PSK", "ECDH", "DH", "RSA"};
1✔
280
      }
281

282
      std::vector<std::string> allowed_signature_methods() const override {
1✔
283
         return {"ECDSA", "RSA", "DSA", "IMPLICIT"};
1✔
284
      }
285

286
      bool allow_tls12() const override { return true; }
1✔
287
};
288

289
inline std::shared_ptr<Botan::TLS::Policy> load_tls_policy(const std::string& policy_type) {
18✔
290
   if(policy_type == "default" || policy_type.empty()) {
18✔
291
      return std::make_shared<Botan::TLS::Policy>();
3✔
292
   } else if(policy_type == "suiteb_128") {
15✔
293
      return std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
294
   } else if(policy_type == "suiteb_192" || policy_type == "suiteb") {
14✔
295
      return std::make_shared<Botan::TLS::NSA_Suite_B_192>();
1✔
296
   } else if(policy_type == "strict") {
13✔
297
      return std::make_shared<Botan::TLS::Strict_Policy>();
1✔
298
   } else if(policy_type == "bsi") {
12✔
299
      return std::make_shared<Botan::TLS::BSI_TR_02102_2>();
×
300
   } else if(policy_type == "datagram") {
12✔
301
      return std::make_shared<Botan::TLS::Strict_Policy>();
×
302
   } else if(policy_type == "all" || policy_type == "everything") {
12✔
303
      return std::make_shared<TLS_All_Policy>();
1✔
304
   }
305

306
   // if something we don't recognize, assume it's a file
307
   std::ifstream policy_stream(policy_type);
11✔
308
   if(!policy_stream.good()) {
11✔
309
      throw Botan_CLI::CLI_Usage_Error("Unknown TLS policy: not a file or known short name");
×
310
   }
311
   return std::make_shared<Botan::TLS::Text_Policy>(policy_stream);
11✔
312
}
11✔
313

314
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc