OpenVPN 3 Core Library
Loading...
Searching...
No Matches
cipheraead.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// Wrap the OpenSSL GCM API.
13
14#pragma once
15
16#include <string>
17
18#include <openssl/objects.h>
19#include <openssl/evp.h>
20
28
29namespace openvpn::OpenSSLCrypto {
31{
32 /* In OpenSSL 3.0 the method that returns EVP_CIPHER, the cipher needs to be
33 * freed afterwards, thus needing a non-const type. In contrast, OpenSSL 1.1.1
34 * and lower returns a const type, needing a const type */
35#if OPENSSL_VERSION_NUMBER < 0x30000000L
36 using evp_cipher_type = const EVP_CIPHER;
37#else
38 using evp_cipher_type = EVP_CIPHER;
39#endif
40 using CIPHER_unique_ptr = std::unique_ptr<evp_cipher_type, decltype(&::EVP_CIPHER_free)>;
41
42 public:
45
47 : ctx(std::exchange(other.ctx, nullptr)), aead_usage_limit_(other.aead_usage_limit_)
48 {
49 }
50
52 {
53 CipherContextAEAD temp(std::move(other));
54 std::swap(ctx, temp.ctx);
55 std::swap(aead_usage_limit_, other.aead_usage_limit_);
56 return *this;
57 }
58
59 OPENVPN_EXCEPTION(openssl_gcm_error);
60
61 // mode parameter for constructor
62 enum
63 {
66 DECRYPT = 0
67 };
68
69 // OpenSSL cipher constants
70 enum : size_t
71 {
72 IV_LEN = 12,
73 AUTH_TAG_LEN = 16
74 };
75
76 bool constexpr requires_authtag_at_end()
77 {
78 return false;
79 }
80
81 CipherContextAEAD() = default;
82
87
88 void init(SSLLib::Ctx libctx,
89 const CryptoAlgs::Type alg,
90 const unsigned char *key,
91 const unsigned int keysize,
92 const int mode)
93 {
95 unsigned int ckeysz = 0;
96 CIPHER_unique_ptr ciph(cipher_type(libctx, alg, ckeysz), EVP_CIPHER_free);
97
98 if (!ciph)
99 OPENVPN_THROW(openssl_gcm_error, CryptoAlgs::name(alg) << ": not usable");
100
101 if (ckeysz > keysize)
102 throw openssl_gcm_error("insufficient key material");
103 ctx = EVP_CIPHER_CTX_new();
104 EVP_CIPHER_CTX_reset(ctx);
105 switch (mode)
106 {
107 case ENCRYPT:
108 if (!EVP_EncryptInit_ex(ctx, ciph.get(), nullptr, key, nullptr))
109 {
112 throw openssl_gcm_error("EVP_EncryptInit_ex (init)");
113 }
114 break;
115 case DECRYPT:
116 if (!EVP_DecryptInit_ex(ctx, ciph.get(), nullptr, key, nullptr))
117 {
120 throw openssl_gcm_error("EVP_DecryptInit_ex (init)");
121 }
122 break;
123 default:
124 throw openssl_gcm_error("bad mode");
125 }
126 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, IV_LEN, nullptr) != 1)
127 {
130 throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl set IV len");
131 }
132 aead_usage_limit_ = {alg};
133 }
134
135 void encrypt(const unsigned char *input,
136 unsigned char *output,
137 size_t length,
138 const unsigned char *iv,
139 unsigned char *tag,
140 const unsigned char *ad,
141 size_t ad_len)
142 {
143 int len;
144 int ciphertext_len;
145
147 if (!EVP_EncryptInit_ex(ctx, nullptr, nullptr, nullptr, iv))
148 {
150 throw openssl_gcm_error("EVP_EncryptInit_ex (reset)");
151 }
152 if (!EVP_EncryptUpdate(ctx, nullptr, &len, ad, int(ad_len)))
153 {
155 throw openssl_gcm_error("EVP_EncryptUpdate AD");
156 }
157 if (!EVP_EncryptUpdate(ctx, output, &len, input, int(length)))
158 {
160 throw openssl_gcm_error("EVP_EncryptUpdate data");
161 }
162 ciphertext_len = len;
163 if (!EVP_EncryptFinal_ex(ctx, output + len, &len))
164 {
166 throw openssl_gcm_error("EVP_EncryptFinal_ex");
167 }
168 ciphertext_len += len;
169 if ((size_t)ciphertext_len != length)
170 {
171 throw openssl_gcm_error("encrypt size inconsistency");
172 }
173 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, tag))
174 {
176 throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl get tag");
177 }
178 aead_usage_limit_.update(length + ad_len);
179 }
180
181
184 {
185 return aead_usage_limit_;
186 }
187
188
202 bool decrypt(const unsigned char *input,
203 unsigned char *output,
204 size_t length,
205 const unsigned char *iv,
206 const unsigned char *tag,
207 const unsigned char *ad,
208 size_t ad_len)
209 {
210 if (!tag)
211 {
212 /* Tag is at the end of input, check that input is large enough to hold the tag */
213 if (length < AUTH_TAG_LEN)
214 {
215 throw openssl_gcm_error("decrypt input length too short");
216 }
217
218 length = length - AUTH_TAG_LEN;
219 tag = input + length;
220 }
221
223 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, nullptr, iv))
224 {
226 throw openssl_gcm_error("EVP_DecryptInit_ex (reset)");
227 }
228
229 int len;
230 if (!EVP_DecryptUpdate(ctx, nullptr, &len, ad, int(ad_len)))
231 {
233 throw openssl_gcm_error("EVP_DecryptUpdate AD");
234 }
235 if (!EVP_DecryptUpdate(ctx, output, &len, input, int(length)))
236 {
238 throw openssl_gcm_error("EVP_DecryptUpdate data");
239 }
240
241 int plaintext_len = len;
244 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN, const_cast<unsigned char *>(tag)))
245 {
247 throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl set tag");
248 }
249 if (!EVP_DecryptFinal_ex(ctx, output + len, &len))
250 {
252 return false;
253 }
254 plaintext_len += len;
255 if (static_cast<size_t>(plaintext_len) != length)
256 {
257 throw openssl_gcm_error("decrypt size inconsistency");
258 }
259 return true;
260 }
261
262 bool is_initialized() const
263 {
264 return ctx != nullptr;
265 }
266
267 static bool is_supported(SSLLib::Ctx libctx, const CryptoAlgs::Type alg)
268 {
269 unsigned int keysize = 0;
270 CIPHER_unique_ptr cipher(cipher_type(libctx, alg, keysize), EVP_CIPHER_free);
271 return (bool)cipher;
272 }
273
274
275 private:
277 const CryptoAlgs::Type alg,
278 unsigned int &keysize)
279 {
280 switch (alg)
281 {
283 keysize = 16;
284 return EVP_CIPHER_fetch(libctx, "AES-128-GCM", nullptr);
286 keysize = 24;
287 return EVP_CIPHER_fetch(libctx, "AES-192-GCM", nullptr);
289 keysize = 32;
290 return EVP_CIPHER_fetch(libctx, "AES-256-GCM", nullptr);
292 keysize = 32;
293 return EVP_CIPHER_fetch(libctx, "CHACHA20-POLY1305", nullptr);
294 default:
295 keysize = 0;
296 return nullptr;
297 }
298 }
299
301 {
302 EVP_CIPHER_CTX_free(ctx);
303 ctx = nullptr;
304 }
305
306 void check_initialized() const
307 {
308#ifdef OPENVPN_ENABLE_ASSERT
309 if (!ctx)
310 throw openssl_gcm_error("uninitialized");
311#endif
312 }
313
314 EVP_CIPHER_CTX *ctx = nullptr;
316};
317} // namespace openvpn::OpenSSLCrypto
void update(const std::size_t outlen)
static evp_cipher_type * cipher_type(SSLLib::Ctx libctx, const CryptoAlgs::Type alg, unsigned int &keysize)
const Crypto::AEADUsageLimit & get_usage_limit()
static bool is_supported(SSLLib::Ctx libctx, const CryptoAlgs::Type alg)
void init(SSLLib::Ctx libctx, const CryptoAlgs::Type alg, const unsigned char *key, const unsigned int keysize, const int mode)
CipherContextAEAD(CipherContextAEAD &&other) noexcept
bool decrypt(const unsigned char *input, unsigned char *output, size_t length, const unsigned char *iv, const unsigned char *tag, const unsigned char *ad, size_t ad_len)
std::unique_ptr< evp_cipher_type, decltype(&::EVP_CIPHER_free)> CIPHER_unique_ptr
CipherContextAEAD(const CipherContextAEAD &)=delete
CipherContextAEAD & operator=(const CipherContextAEAD &)=delete
CipherContextAEAD & operator=(CipherContextAEAD &&other)
void encrypt(const unsigned char *input, unsigned char *output, size_t length, const unsigned char *iv, unsigned char *tag, const unsigned char *ad, size_t ad_len)
static void EVP_CIPHER_free(const EVP_CIPHER *cipher)
Definition compat.hpp:83
static const EVP_CIPHER * EVP_CIPHER_fetch(void *ctx, const char *algorithm, const char *properties)
Definition compat.hpp:54
#define OPENVPN_THROW(exc, stuff)
const char * name(const KeyDerivation kd)
void openssl_clear_error_stack()
Definition error.hpp:247
static const char * input[]