OpenVPN 3 Core Library
Loading...
Searching...
No Matches
crypto_aead_epoch.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_EPOCH_H
15#define OPENVPN_CRYPTO_CRYPTO_AEAD_EPOCH_H
16
17#include <array>
18
29
31
32OPENVPN_EXCEPTION(aead_epoch_error);
33
34template <typename CRYPTO_API>
36{
37
40
41 public:
42 Crypto(SSLLib::Ctx libctx_arg,
43 CryptoDCSettingsData dc_settings_data,
44 const Frame::Ptr &frame_arg,
45 const SessionStats::Ptr &stats_arg)
46 : dc_settings(dc_settings_data),
47 frame(frame_arg),
48 stats(stats_arg),
49 libctx(libctx_arg)
50 {
51 }
52
53
54 // Encrypt/Decrypt
55 // returns true if packet ID is close to wrapping
56 bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
57 {
58 // only process non-null packets
59 if (buf.empty())
60 {
61 return false;
62 }
63
65
66 auto &encrypt_ctx = dce.encrypt();
67
68 /* header of the packet. op32 (opcode + peer-id) + 8 byte of epoch + epoch counter */
69 BufferAllocated pkt_header{4 + 8};
70
71 pkt_header.write(op32, 4);
72 encrypt_ctx.pid.write_next(pkt_header);
73
74 std::array<uint8_t, EpochDataChannelCryptoContext::IV_SIZE> calculated_iv{};
75 encrypt_ctx.calculate_iv(pkt_header.data() + 4, calculated_iv);
76
77 // encrypt to work buf
79 if (work_encrypt.max_size() < buf.size())
80 throw aead_epoch_error("encrypt work buffer too small");
81
82 unsigned char *work_data = work_encrypt.write_alloc(buf.size());
83
84 // alloc auth tag in buffer where it needs to be
85 uint8_t *auth_tag = work_encrypt.write_alloc(CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
86
87 // encrypt. Epoch data always uses full header for authenticated data
88 encrypt_ctx.cipher.encrypt(buf.data(), work_data, buf.size(), calculated_iv.data(), auth_tag, pkt_header.data(), pkt_header.size());
89
90 buf.swap(work_encrypt);
91
92 // prepend additional data from the pkt_header but without the opcode and peer-id (first 4 bytes)
93 buf.prepend(pkt_header.c_data() + 4, pkt_header.size() - 4);
94
95 return dce.should_renegotiate();
96 }
97
98 Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
99 {
100 // only process non-null packets but report empty packets as success
101 if (buf.empty())
102 {
103 return Error::SUCCESS;
104 }
105
106
107
108 if (buf.size() < PacketIDData::size(true))
109 {
110 /* Packet is too small to even have a packet id */
112 }
113
114 // Reconstruct header since we don't get the continuous memory that we received from wire but already
115 // split into op32 and the rest of the packet.
116 BufferAllocated pkt_header{4 + 8};
117 pkt_header.write(op32, 4);
118 auto *packet_id = pkt_header.write_alloc(8);
119 buf.read(packet_id, 8);
120
121 // Extract epoch from packet
122 ConstBuffer packet_id_buf{packet_id, 8, true};
123 PacketIDData pid{true};
124 pid.read(packet_id_buf);
125
126 auto *decrypt_ctx = dce.lookup_decrypt_key(pid.get_epoch());
127
128 if (!decrypt_ctx)
129 {
130 // failed to look up the epoch. Report error.
132 }
133
134 // calculate IV from implicit IV and packet ID
135 std::array<uint8_t, EpochDataChannelCryptoContext::IV_SIZE> calculated_iv{};
136 decrypt_ctx->calculate_iv(packet_id, calculated_iv);
137
138 // initialize work buffer.
140 if (work_decrypt.max_size() < buf.size())
141 throw aead_epoch_error("decrypt work buffer too small");
142
143 // decrypt from buf -> work
144 if (!decrypt_ctx->cipher.decrypt(buf.c_data(), work_decrypt.data(), buf.size(), calculated_iv.data(), nullptr, pkt_header.data(), pkt_header.size()))
145 {
146 buf.reset_size();
148 }
149
150 work_decrypt.set_size(buf.size() - CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN);
151
152 // verify packet ID
153 if (!decrypt_ctx->pid.test_add(pid, now, stats))
154 {
155 buf.reset_size();
156 return Error::REPLAY_ERROR;
157 }
158
159 // Check if the other side has moved its epoch send key further
160 // and we need to adjust our active recv key and generate new future keys
161 dce.replace_update_recv_key(decrypt_ctx->epoch, stats);
162
163 // return cleartext result in buf
164 buf.swap(work_decrypt);
165
166 return Error::SUCCESS;
167 }
168
169 // Initialization
170
171 void init_cipher(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
172 {
174 {
175 throw aead_epoch_error("AEAD Epoch requires epoch keys to be in use");
176 }
177
178 /* Initialise the epoch key management with the encrypt and decrypt key as epoch 1 keys */
179 dce = {dc_settings.cipher(), std::move(encrypt_key), std::move(decrypt_key), libctx};
180 }
181
182 void init_hmac(StaticKey &&encrypt_key,
183 StaticKey &&decrypt_key) override
184 {
185 /* Implicit IVs are derived in DataChannelEpoch class and AEAD does not use
186 * a separate HMAC, so this is just a dummy that does nothing.*/
187 }
188
189 void init_pid(const char *recv_name,
190 const int recv_unit,
191 const SessionStats::Ptr &recv_stats_arg) override
192 {
193 }
194
195 unsigned int defined() const override
196 {
197 unsigned int ret = CRYPTO_DEFINED;
198
201 return ret;
202 }
203
204 bool consider_compression([[maybe_unused]] const CompressContext &comp_ctx) override
205 {
206 return true;
207 }
208
209 void rekey(const RekeyType type) override
210 {
211 }
212
213 // Force using a new epoch on send. Currently mainly used for unit testing
215 {
217 }
218
219 private:
225};
226
227template <typename CRYPTO_API>
229{
230 public:
232
234 CryptoDCSettingsData dc_settings_data,
235 const Frame::Ptr &frame_arg,
236 const SessionStats::Ptr &stats_arg)
237 : CryptoDCContext(dc_settings_data.key_derivation()),
238 dc_settings(std::move(dc_settings_data)),
239 frame(frame_arg),
240 stats(stats_arg),
241 libctx(libctx_arg)
242 {
243 /* Check if the cipher is legal for AEAD and otherwise throw */
244 legal_dc_cipher(dc_settings.cipher());
246 }
247
248 CryptoDCInstance::Ptr new_obj([[maybe_unused]] const unsigned int key_id) override
249 {
251 }
252
253 // cipher/HMAC/key info
255 {
256 return dc_settings;
257 }
258
259 // Info for ProtoContext::link_mtu_adjust
260 size_t encap_overhead() const override
261 {
262 /* encap_overhead does not really return the encapsulation overhead of this channel as it ignores
263 * the packet-id and 4 byte opcode.
264 *
265 * We keep this in line with the normal AEAD data channel defined in crypto_aead_epoch.hpp, and the
266 * keep the difference to that correct. The difference in overhead are the 4 bytes in the larger packet counter/epoch */
267 return CRYPTO_API::CipherContextAEAD::AUTH_TAG_LEN + 4;
268 }
269
270 private:
275};
276} // namespace openvpn::AEADEpoch
277
278#endif
CryptoDCSettingsData crypto_info() override
CryptoDCInstance::Ptr new_obj(const unsigned int key_id) override
CryptoContext(SSLLib::Ctx libctx_arg, CryptoDCSettingsData dc_settings_data, const Frame::Ptr &frame_arg, const SessionStats::Ptr &stats_arg)
void init_cipher(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
void init_pid(const char *recv_name, const int recv_unit, const SessionStats::Ptr &recv_stats_arg) override
void rekey(const RekeyType type) override
Crypto(SSLLib::Ctx libctx_arg, CryptoDCSettingsData dc_settings_data, const Frame::Ptr &frame_arg, const SessionStats::Ptr &stats_arg)
Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
bool consider_compression(const CompressContext &comp_ctx) override
CryptoDCSettingsData dc_settings
void init_hmac(StaticKey &&encrypt_key, StaticKey &&decrypt_key) override
unsigned int defined() const override
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
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
bool empty() const
Returns true if the buffer is empty.
Definition buffer.hpp:1236
void write(const T *data, const size_t size)
Write data to the buffer.
Definition buffer.hpp:1563
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
EpochDataChannelDecryptContext * lookup_decrypt_key(uint16_t epoch)
void replace_update_recv_key(std::uint16_t new_epoch, const SessionStats::Ptr &stats_arg)
EpochDataChannelEncryptContext & encrypt()
size_t prepare(const unsigned int context, Buffer &buf) const
Definition frame.hpp:266
#define OPENVPN_EXCEPTION(C)
bool defined(const Type type)
void calculate_iv(uint8_t *packet_id, std::array< uint8_t, IV_SIZE > &iv_dest)
void read(ConstBuffer &buf)
constexpr std::size_t size() const
std::string ret