OpenVPN 3 Core Library
Loading...
Searching...
No Matches
cryptoalgs.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Crypto algorithms
13
14#ifndef OPENVPN_CRYPTO_CRYPTOALGS_H
15#define OPENVPN_CRYPTO_CRYPTOALGS_H
16
17#include <functional>
18#include <string>
19#include <list>
20
27
29
31OPENVPN_SIMPLE_EXCEPTION(crypto_alg_index);
32
33enum class KeyDerivation
34{
38};
39
40inline const char *name(const KeyDerivation kd)
41{
42 switch (kd)
43 {
45 return "[PRF undefined]";
47 return "OpenVPN PRF";
49 return "TLS Keying Material Exporter [RFC5705]";
50 default:
51 return "Unknown";
52 }
53}
54
87
94
96{
97 F_CIPHER = (1 << 0), // alg is a cipher
98 F_DIGEST = (1 << 1), // alg is a digest
99 F_ALLOW_DC = (1 << 2) // alg may be used in OpenVPN data channel
101
102// size in bytes of AEAD "nonce tail" normally taken from
103// HMAC key material
104enum
105{
108
109class Alg
110{
111 public:
112 constexpr Alg(const char *name,
113 const unsigned int flags,
114 const Mode mode,
115 const unsigned int size,
116 const unsigned int iv_length,
117 const unsigned int block_size,
118 uint64_t aead_usage_limit)
119
120 : name_(name),
121 flags_(flags),
122 mode_(mode),
123 size_(size),
127 {
128 }
129
130 const char *name() const
131 {
132 return name_;
133 }
134 unsigned int flags() const
135 {
136 return flags_;
137 }
138 Mode mode() const
139 {
140 return mode_;
141 }
142 size_t size() const
143 {
144 return size_;
145 } // digest size
146 size_t key_length() const
147 {
148 return size_;
149 } // cipher key length
150 size_t iv_length() const
151 {
152 return iv_length_;
153 } // cipher only
154 size_t block_size() const
155 {
156 return block_size_;
157 } // cipher only
158 bool dc_cipher() const
159 {
160 return (flags_ & F_CIPHER) && (flags_ & F_ALLOW_DC);
161 }
162 bool dc_digest() const
163 {
164 return (flags_ & F_DIGEST) && (flags_ & F_ALLOW_DC);
165 }
166
167 void allow_dc(bool allow)
168 {
169 if (allow)
171 else
172 flags_ &= ~F_ALLOW_DC;
173 }
174
177 uint64_t aead_usage_limit() const
178 {
179 return aead_usage_limit_;
180 }
181
182 private:
183 const char *name_;
184 unsigned int flags_;
186 unsigned int size_;
187 unsigned int iv_length_;
188 unsigned int block_size_;
190};
191
193static constexpr uint64_t gcm_limit = (1ull << 36) - 1;
194
195inline std::array<Alg, Type::SIZE> algs = {
196 // clang-format off
197 Alg{"none", F_CIPHER|F_DIGEST, CBC_HMAC, 0, 0, 0, 0 },
198 Alg{"AES-128-CBC", F_CIPHER, CBC_HMAC, 16, 16, 16, 0 },
199 Alg{"AES-192-CBC", F_CIPHER, CBC_HMAC, 24, 16, 16, 0 },
200 Alg{"AES-256-CBC", F_CIPHER, CBC_HMAC, 32, 16, 16, 0 },
201 Alg{"DES-CBC", F_CIPHER, CBC_HMAC, 8, 8, 8, 0 },
202 Alg{"DES-EDE3-CBC", F_CIPHER, CBC_HMAC, 24, 8, 8, 0 },
203 Alg{"BF-CBC", F_CIPHER, CBC_HMAC, 16, 8, 8, 0 },
204 Alg{"AES-256-CTR", F_CIPHER, MODE_UNDEF, 32, 16, 16, 0 },
205 Alg{"AES-128-GCM", F_CIPHER, AEAD, 16, 12, 16, gcm_limit },
206 Alg{"AES-192-GCM", F_CIPHER, AEAD, 24, 12, 16, gcm_limit },
207 Alg{"AES-256-GCM", F_CIPHER, AEAD, 32, 12, 16, gcm_limit },
208 Alg{"CHACHA20-POLY1305", F_CIPHER, AEAD, 32, 12, 16, 0 },
209 Alg{"MD4", F_DIGEST, MODE_UNDEF, 16, 0, 0, 0 },
210 Alg{"MD5", F_DIGEST, MODE_UNDEF, 16, 0, 0, 0 },
211 Alg{"SHA1", F_DIGEST, MODE_UNDEF, 20, 0, 0, 0 },
212 Alg{"SHA224", F_DIGEST, MODE_UNDEF, 28, 0, 0, 0 },
213 Alg{"SHA256", F_DIGEST, MODE_UNDEF, 32, 0, 0, 0 },
214 Alg{"SHA384", F_DIGEST, MODE_UNDEF, 48, 0, 0, 0 },
215 Alg{"SHA512", F_DIGEST, MODE_UNDEF, 64, 0, 0, 0 }
216 // clang-format on
217};
218
219inline bool defined(const Type type)
220{
221 return type != NONE;
222}
223
224inline const Alg &get_index(const size_t i)
225{
226 if (unlikely(i >= algs.size()))
227 throw crypto_alg_index();
228 return algs[i];
229}
230
231inline const Alg *get_ptr(const Type type)
232{
233 const Alg &alg_ref = get_index(static_cast<size_t>(type));
234 return &alg_ref;
235}
236
237inline const Alg &get(const Type type)
238{
239 return get_index(static_cast<size_t>(type));
240}
241
242inline std::size_t for_each(std::function<bool(Type, const Alg &)> fn)
243{
244 std::size_t count = 0;
245 for (std::size_t i = 0; i < algs.size(); ++i)
246 if (fn(static_cast<Type>(i), algs[i]))
247 count++;
248 return count;
249}
250
251inline Type lookup(const std::string &name)
252{
253 for (size_t i = 0; i < algs.size(); ++i)
254 {
255 if (string::strcasecmp(name, algs[i].name()) == 0)
256 return static_cast<Type>(i);
257 }
258 OPENVPN_THROW(crypto_alg, name << ": not found");
259}
260
261inline const char *name(const Type type, const char *default_name = nullptr)
262{
263 if (type == NONE && default_name)
264 return default_name;
265 else
266 return get(type).name();
267}
268
269inline size_t size(const Type type)
270{
271 const Alg &alg = get(type);
272 return alg.size();
273}
274
275inline size_t key_length(const Type type)
276{
277 const Alg &alg = get(type);
278 return alg.key_length();
279}
280
281inline size_t iv_length(const Type type)
282{
283 const Alg &alg = get(type);
284 return alg.iv_length();
285}
286
287inline size_t block_size(const Type type)
288{
289 const Alg &alg = get(type);
290 return alg.block_size();
291}
292
293inline Mode mode(const Type type)
294{
295 const Alg &alg = get(type);
296 return alg.mode();
297}
298
299inline uint64_t aead_usage_limit(const Type type)
300{
301 const Alg &alg = get(type);
302 return alg.aead_usage_limit();
303}
304
305inline Type legal_dc_cipher(const Type type)
306{
307 const Alg &alg = get(type);
308 if (!alg.dc_cipher())
309 OPENVPN_THROW(crypto_alg, alg.name() << ": bad cipher for data channel use");
310 return type;
311}
312
313inline Type legal_dc_digest(const Type type)
314{
315 const Alg &alg = get(type);
316 if (!alg.dc_digest())
317 OPENVPN_THROW(crypto_alg, alg.name() << ": bad digest for data channel use");
318 return type;
319}
320
321inline Type dc_cbc_cipher(const Type type)
322{
323 const Alg &alg = get(type);
324 if (!(alg.mode() == CBC_HMAC))
325 OPENVPN_THROW(crypto_alg, alg.name() << ": bad cipher for data channel use");
326 return type;
327}
328
329inline Type dc_cbc_hash(const Type type)
330{
331 const Alg &alg = get(type);
332 if (!(alg.flags() & F_DIGEST))
333 OPENVPN_THROW(crypto_alg, alg.name() << ": bad digest for data channel use");
334 return type;
335}
336
337inline void allow_dc_algs(const std::list<Type> types)
338{
339 for (auto &alg : algs)
340 alg.allow_dc(false);
341 for (auto &type : types)
342 algs.at(type).allow_dc(true);
343}
344
354template <typename CRYPTO_API>
355inline void allow_default_dc_algs(SSLLib::Ctx libctx, bool preferred = false, bool legacy = false)
356{
357 /* Disable all and reenable the ones actually allowed later */
358 for (auto &alg : algs)
359 alg.allow_dc(false);
360
361 CryptoAlgs::for_each([preferred, libctx, legacy](CryptoAlgs::Type type, const CryptoAlgs::Alg &alg) -> bool
362 {
363 /* Defined in the algorithm but not actually related to data channel */
364 if (type == MD4 || type == AES_256_CTR)
365 return false;
366
367 if (preferred && alg.mode() != AEAD)
368 return false;
369
370 if (alg.mode() == AEAD && !CRYPTO_API::CipherContextAEAD::is_supported(libctx, type))
371 return false;
372
373 /* 64 bit block ciphers vulnerable to SWEET32 */
374 if (alg.flags() & F_CIPHER && !legacy && alg.block_size() <= 8)
375 return false;
376
377 /* This excludes MD4 */
378 if (alg.flags() & F_DIGEST && !legacy && alg.size() < 20)
379 return false;
380
381 if ((alg.flags() & F_CIPHER && alg.mode() != AEAD && type != NONE)
382 && !CRYPTO_API::CipherContext::is_supported(libctx, type))
383 return false;
384
385 /* This algorithm has passed all checks, enable it for DC */
386 algs.at(type).allow_dc(true);
387 return true; });
388}
389
398inline bool use_cipher_digest(const Type type)
399{
400 const Alg &alg = get(type);
401 return alg.mode() != AEAD;
402}
403} // namespace openvpn::CryptoAlgs
404
405#endif
size_t block_size() const
const char * name() const
unsigned int flags() const
uint64_t aead_usage_limit() const
constexpr Alg(const char *name, const unsigned int flags, const Mode mode, const unsigned int size, const unsigned int iv_length, const unsigned int block_size, uint64_t aead_usage_limit)
size_t key_length() const
void allow_dc(bool allow)
#define OPENVPN_SIMPLE_EXCEPTION(C)
Definition exception.hpp:75
#define OPENVPN_EXCEPTION(C)
#define OPENVPN_THROW(exc, stuff)
#define unlikely(x)
Definition likely.hpp:22
size_t block_size(const Type type)
static constexpr uint64_t gcm_limit
void allow_dc_algs(const std::list< Type > types)
Type dc_cbc_cipher(const Type type)
const Alg & get(const Type type)
bool use_cipher_digest(const Type type)
bool defined(const Type type)
Mode mode(const Type type)
Type lookup(const std::string &name)
size_t key_length(const Type type)
std::array< Alg, Type::SIZE > algs
const Alg * get_ptr(const Type type)
Type legal_dc_digest(const Type type)
const char * name(const KeyDerivation kd)
Type dc_cbc_hash(const Type type)
void allow_default_dc_algs(SSLLib::Ctx libctx, bool preferred=false, bool legacy=false)
size_t iv_length(const Type type)
size_t size(const Type type)
std::size_t for_each(std::function< bool(Type, const Alg &)> fn)
Type legal_dc_cipher(const Type type)
uint64_t aead_usage_limit(const Type type)
const Alg & get_index(const size_t i)
int strcasecmp(const char *s1, const char *s2)
Definition string.hpp:29