OpenVPN 3 Core Library
Loading...
Searching...
No Matches
crypto_aead.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// OpenVPN AEAD data channel interface
13
14#ifndef OPENVPN_CRYPTO_CRYPTO_AEAD_H
15#define OPENVPN_CRYPTO_CRYPTO_AEAD_H
16
17#include <cstring> // for std::memcpy, std::memset
18
28
29// Sample AES-GCM head:
30// 48000001 00000005 7e7046bd 444a7e28 cc6387b1 64a4d6c1 380275a...
31// [ OP32 ] [seq # ] [ auth tag ] [ payload ... ]
32// [4-byte
33// IV head]
34
36
37namespace openvpn::AEAD {
38
40
41template <typename CRYPTO_API>
43{
44 class Nonce
45 {
46 public:
48 {
49 static_assert(4 + CRYPTO_API::CipherContextAEAD::IV_LEN == sizeof(data),
50 "AEAD IV_LEN inconsistency");
51 ad_op32 = false;
52 std::memset(data, 0, sizeof(data));
53 }
54
66 void set_tail(const StaticKey &sk)
67 {
68 constexpr size_t implicit_iv_len = 8;
69 if (sk.size() < implicit_iv_len)
70 throw aead_error("insufficient key material for nonce tail");
71
72 /* 4 bytes opcode + 4 bytes on wire IV */
73 constexpr size_t implicit_iv_offset = data_offset_pkt_id + (12 - implicit_iv_len);
74 std::memcpy(data + implicit_iv_offset, sk.data(), implicit_iv_len);
75 }
76
77 // for encrypt
78 Nonce(const Nonce &ref, PacketIDDataSend &pid_send, const unsigned char *op32)
79 {
81 std::memcpy(data, ref.data, sizeof(data));
82
84 pid_send.write_next(buf);
85 if (op32)
86 {
87 ad_op32 = true;
88 std::memcpy(data, op32, op32_size);
89 }
90 else
91 ad_op32 = false;
92 }
93
94 // for encrypt
95 void prepend_ad(Buffer &buf, const PacketIDDataSend &pid_send) const
96 {
97 buf.prepend(data + data_offset_pkt_id, pid_send.length());
98 }
99
100 // for decrypt
101 Nonce(const Nonce &ref, const PacketIDDataReceive &recv_pid, Buffer &buf, const unsigned char *op32)
102 {
103 /* Copy opcode and tail of packet ID */
104 std::memcpy(data, ref.data, sizeof(data));
105
106 /* copy dynamic packet of IV into */
107 buf.read(data + data_offset_pkt_id, recv_pid.length());
108 if (op32)
109 {
110 ad_op32 = true;
111 std::memcpy(data, op32, op32_size);
112 }
113 else
114 ad_op32 = false;
115 }
116
117 // for decrypt
119 {
121 const PacketIDData pid = pid_recv.read_next(buf);
122 return pid_recv.test_add(pid, now, stats_arg); // verify packet ID
123 }
124
125 const unsigned char *iv() const
126 {
127 return data + data_offset_pkt_id;
128 }
129
130 const unsigned char *ad() const
131 {
132 return ad_op32 ? data : data + data_offset_pkt_id;
133 }
134
135 size_t ad_len(const PacketIDDataSend &pid_send) const
136 {
137 return (ad_op32 ? op32_size : 0) + pid_send.length();
138 }
139
140 size_t ad_len(const PacketIDDataReceive &pid_recv) const
141 {
142 return (ad_op32 ? op32_size : 0) + pid_recv.length();
143 }
144
145
146 private:
147 bool ad_op32; // true if AD (authenticated data) includes op32 opcode
148
149 // Sample data:
150 // [ OP32 (optional) ] [ pkt ID ] [ nonce tail ]
151 // [ 48 00 00 01 ] [ 00 00 00 05 ] [ 7f 45 64 db 33 5b 6c 29 ]
152 unsigned char data[16];
153 static constexpr std::size_t data_offset_pkt_id = 4;
154 static constexpr std::size_t op32_size = 4;
155 };
156
157 struct Encrypt
158 {
159 typename CRYPTO_API::CipherContextAEAD impl;
163 };
164
165 struct Decrypt
166 {
167 typename CRYPTO_API::CipherContextAEAD impl;
171 };
172
173 public:
175
176 Crypto(SSLLib::Ctx libctx_arg,
177 CryptoDCSettingsData dc_settings_data,
178 const Frame::Ptr &frame_arg,
179 const SessionStats::Ptr &stats_arg)
180 : dc_settings(dc_settings_data),
181 frame(frame_arg),
182 stats(stats_arg),
183 libctx(libctx_arg)
184 {
185 }
186
187 // Encrypt/Decrypt
188
189 // returns true if packet ID is close to wrapping
190 bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
191 {
192 // only process non-null packets
193 if (buf.size())
194 {
195 // build nonce/IV/AD
196 Nonce nonce(e.nonce, e.pid_send, op32);
197
198 // encrypt to work buf
200 if (e.work.max_size() < buf.size())
201 throw aead_error("encrypt work buffer too small");
202
203
204 unsigned char *work_data = e.work.write_alloc(buf.size());
205
206 unsigned char *auth_tag_tmp = nullptr;
207 // alloc auth tag in buffer at the start of the packet
208 // Create a temporary auth tag at the end if the implementation and mode require it
209
210 unsigned char *auth_tag = e.work.prepend_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
211 if (e.impl.requires_authtag_at_end())
212 {
213 auth_tag_tmp = e.work.write_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
214 }
215
216 // encrypt
217 e.impl.encrypt(buf.data(), work_data, buf.size(), nonce.iv(), auth_tag, nonce.ad(), nonce.ad_len(e.pid_send));
218
219 if (auth_tag_tmp)
220 {
221 /* move the auth tag to the front */
222 std::memcpy(auth_tag, auth_tag_tmp, CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
223 /* Ignore the auth tag at the end */
224 e.work.inc_size(-CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
225 }
226
227 buf.swap(e.work);
228
229 // prepend additional data
230 nonce.prepend_ad(buf, e.pid_send);
231 }
232 return e.pid_send.wrap_warning() || e.impl.get_usage_limit().usage_limit_warn();
233 }
234
235 Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
236 {
237 // only process non-null packets
238 if (buf.size())
239 {
240 // get nonce/IV/AD
241 Nonce nonce(d.nonce, d.pid_recv, buf, op32);
242
243 // get auth tag if it is at the front. If the auth tag is at the end
244 // the decrypt function will just treat it as part of the input
245 unsigned char *auth_tag = nullptr;
246
247 auth_tag = buf.read_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
248
249 // initialize work buffer.
251 if (d.work.max_size() < buf.size())
252 throw aead_error("decrypt work buffer too small");
253
254 if (auth_tag && e.impl.requires_authtag_at_end())
255 {
256 unsigned char *auth_tag_end = buf.write_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
257 std::memcpy(auth_tag_end, auth_tag, CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
258 auth_tag = nullptr;
259 }
260
261 // decrypt from buf -> work
262 if (!d.impl.decrypt(buf.c_data(), d.work.data(), buf.size(), nonce.iv(), auth_tag, nonce.ad(), nonce.ad_len(d.pid_recv)))
263 {
264 buf.reset_size();
266 }
267
268 if (e.impl.requires_authtag_at_end())
269 {
270 d.work.set_size(buf.size() - CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
271 }
272 else
273 {
274 d.work.set_size(buf.size());
275 }
276
277 // verify packet ID
278 if (!nonce.verify_packet_id(d.pid_recv, now, stats))
279 {
280 buf.reset_size();
281 return Error::REPLAY_ERROR;
282 }
283
284 // return cleartext result in buf
285 buf.swap(d.work);
286 }
287 return Error::SUCCESS;
288 }
289
290 // Initialization
291
292 // TODO: clamp_to_default probably will cause an error further along if triggered, investigate
293 void init_cipher(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
294 {
295 e.impl.init(libctx,
297 encrypt_key.data(),
298 clamp_to_default<unsigned int>(encrypt_key.size(), 0),
299 CRYPTO_API::CipherContextAEAD::ENCRYPT);
300 d.impl.init(libctx,
302 decrypt_key.data(),
303 clamp_to_default<unsigned int>(decrypt_key.size(), 0),
304 CRYPTO_API::CipherContextAEAD::DECRYPT);
305 }
306
307 void init_hmac(StaticKey &&encrypt_key,
308 StaticKey &&decrypt_key) override
309 {
310 e.nonce.set_tail(encrypt_key);
311 d.nonce.set_tail(decrypt_key);
312 }
313
314 void init_pid(const char *recv_name,
315 const int recv_unit,
316 const SessionStats::Ptr &recv_stats_arg) override
317 {
319 d.pid_recv.init(recv_name, recv_unit, false);
320 stats = recv_stats_arg;
321 }
322
323 // Indicate whether or not cipher/digest is defined
324
325 unsigned int defined() const override
326 {
327 unsigned int ret = CRYPTO_DEFINED;
328
329 // AEAD mode doesn't use HMAC, but we still indicate HMAC_DEFINED
330 // because we want to use the HMAC keying material for the AEAD nonce tail.
333 return ret;
334 }
335
336 bool consider_compression(const CompressContext &comp_ctx) override
337 {
338 return true;
339 }
340
341 // Rekeying
342 void rekey(const typename Base::RekeyType type) override
343 {
344 }
345
346 private:
353};
354
355template <typename CRYPTO_API>
357{
358 public:
360
362 CryptoDCSettingsData dc_settings_data,
363 const Frame::Ptr &frame_arg,
364 const SessionStats::Ptr &stats_arg)
365 : CryptoDCContext(dc_settings_data.key_derivation()),
366 dc_settings(std::move(dc_settings_data)),
367 frame(frame_arg),
368 stats(stats_arg),
369 libctx(libctx_arg)
370 {
371 /* Check if the cipher is legal for AEAD and otherwise throw */
372 legal_dc_cipher(dc_settings.cipher());
374 }
375
376 CryptoDCInstance::Ptr new_obj(const unsigned int key_id) override
377 {
379 }
380
381 // cipher/HMAC/key info
383 {
384 return dc_settings;
385 }
386
387 // Info for ProtoContext::link_mtu_adjust
388
389 size_t encap_overhead() const override
390 {
391 return CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN;
392 }
393
394 private:
399};
400} // namespace openvpn::AEAD
401
402#endif
CryptoContext(SSLLib::Ctx libctx_arg, CryptoDCSettingsData dc_settings_data, const Frame::Ptr &frame_arg, const SessionStats::Ptr &stats_arg)
size_t encap_overhead() const override
CryptoDCSettingsData crypto_info() override
CryptoDCSettingsData dc_settings
RCPtr< CryptoContext > Ptr
CryptoDCInstance::Ptr new_obj(const unsigned int key_id) override
size_t ad_len(const PacketIDDataSend &pid_send) const
void set_tail(const StaticKey &sk)
void prepend_ad(Buffer &buf, const PacketIDDataSend &pid_send) const
static constexpr std::size_t op32_size
const unsigned char * ad() const
static constexpr std::size_t data_offset_pkt_id
size_t ad_len(const PacketIDDataReceive &pid_recv) const
Nonce(const Nonce &ref, const PacketIDDataReceive &recv_pid, Buffer &buf, const unsigned char *op32)
bool verify_packet_id(PacketIDDataReceive &pid_recv, const PacketIDControl::time_t now, const SessionStats::Ptr &stats_arg)
const unsigned char * iv() const
Nonce(const Nonce &ref, PacketIDDataSend &pid_send, const unsigned char *op32)
void init_pid(const char *recv_name, const int recv_unit, const SessionStats::Ptr &recv_stats_arg) override
SessionStats::Ptr stats
CryptoDCInstance Base
bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
void init_cipher(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
unsigned int defined() const override
Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
void rekey(const typename Base::RekeyType type) override
void init_hmac(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
Crypto(SSLLib::Ctx libctx_arg, CryptoDCSettingsData dc_settings_data, const Frame::Ptr &frame_arg, const SessionStats::Ptr &stats_arg)
bool consider_compression(const CompressContext &comp_ctx) override
CryptoDCSettingsData dc_settings
void swap(BufferAllocatedType< T_ > &other)
Swaps the contents of this BufferAllocatedType object with another BufferAllocatedType object.
Definition buffer.hpp:1799
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1194
T * prepend_alloc(const size_t size)
Allocate space for prepending data to the buffer.
Definition buffer.hpp:1597
void inc_size(const size_t delta)
Increment the size of the array (usually used in a similar context to set_size such as after mutable_...
Definition buffer.hpp:1392
size_t max_size() const
Return the maximum allowable size value in T objects given the current offset (without considering re...
Definition buffer.hpp:1377
T * write_alloc(const size_t size)
Allocate space for writing data to the buffer.
Definition buffer.hpp:1587
void prepend(const T *data, const size_t size)
Prepend data to the buffer.
Definition buffer.hpp:1575
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1242
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1450
auto * read_alloc(const size_t size)
Allocate memory and read data from the buffer into the allocated memory.
Definition buffer.hpp:1343
void set_size(const size_t size)
After an external method, operating on the array as a mutable unsigned char buffer,...
Definition buffer.hpp:1384
void reset_size()
Resets the size of the buffer to zero.
Definition buffer.hpp:1170
void read(NCT *data, const size_t size)
Read data from the buffer into the specified memory location.
Definition buffer.hpp:1331
CryptoAlgs::KeyDerivation key_derivation
Definition cryptodc.hpp:179
void set_digest(CryptoAlgs::Type digest)
Definition cryptodc.hpp:110
CryptoAlgs::Type cipher() const
Definition cryptodc.hpp:120
size_t prepare(const unsigned int context, Buffer &buf) const
Definition frame.hpp:266
PacketIDData read_next(Buffer &buf) const
void init(const char *name_arg, const int unit_arg, bool wide_arg)
std::size_t constexpr length() const
bool test_add(const PacketIDData &pin, const Time::base_type now, const SessionStats::Ptr &stats)
constexpr std::size_t length() const
const unsigned char * data() const
size_t size() const
#define OPENVPN_EXCEPTION(C)
bool defined(const Type type)
OutT clamp_to_default(InT inVal, OutT defVal)
Adjusts the input value to the default if the input value exceeds the range of the output type.
CRYPTO_API::CipherContextAEAD impl
CRYPTO_API::CipherContextAEAD impl
static constexpr std::size_t long_id_size
std::string ret