forked from OpenVPN/openvpn3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tls_crypt.hpp
310 lines (241 loc) · 9.85 KB
/
tls_crypt.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2022 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// OpenVPN TLS-Crypt classes
#ifndef OPENVPN_CRYPTO_TLSCRYPT_H
#define OPENVPN_CRYPTO_TLSCRYPT_H
#include <string>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/memneq.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/ssl/psid.hpp>
namespace openvpn {
// OpenVPN protocol HMAC usage for HMAC/CTR integrity checking and tls-crypt
// Control packet format when tls-crypt is enabled:
// [OP] [PSID] [PID] [HMAC] [...]
template <typename CRYPTO_API>
class TLSCrypt
{
public:
OPENVPN_SIMPLE_EXCEPTION(ovpn_tls_crypt_context_digest_size);
OPENVPN_SIMPLE_EXCEPTION(ovpn_tls_crypt_context_bad_sizing);
OPENVPN_SIMPLE_EXCEPTION(ovpn_tls_crypt_wrong_mode);
TLSCrypt()
: mode(CRYPTO_API::CipherContext::MODE_UNDEF)
{
}
TLSCrypt(SSLLib::Ctx libctx, const CryptoAlgs::Type digest, const StaticKey &key_hmac, const CryptoAlgs::Type cipher, const StaticKey &key_crypt, const int mode)
{
init(libctx, digest, key_hmac, cipher, key_crypt, mode);
}
bool defined() const
{
return ctx_hmac.is_initialized() && ctx_crypt.is_initialized();
}
// size of out buffer to pass to hmac
size_t output_hmac_size() const
{
return ctx_hmac.size();
}
void init(SSLLib::Ctx libctx, const CryptoAlgs::Type digest, const StaticKey &key_hmac, const CryptoAlgs::Type cipher, const StaticKey &key_crypt, const int mode_arg)
{
const CryptoAlgs::Alg &alg_hmac = CryptoAlgs::get(digest);
// check that key is large enough
if (key_hmac.size() < alg_hmac.size())
throw ovpn_tls_crypt_context_digest_size();
// initialize HMAC context with digest type and key
ctx_hmac.init(digest, key_hmac.data(), alg_hmac.size());
// initialize Cipher context with cipher, key and mode
ctx_crypt.init(libctx, cipher, key_crypt.data(), mode_arg);
mode = mode_arg;
}
bool hmac_gen(unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len)
{
hmac_pre(header, header_len, payload, payload_len);
ctx_hmac.final(header + header_len);
return true;
}
bool hmac_cmp(const unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len)
{
unsigned char local_hmac[CRYPTO_API::HMACContext::MAX_HMAC_SIZE];
hmac_pre(header, header_len, payload, payload_len);
ctx_hmac.final(local_hmac);
return !crypto::memneq(header + header_len, local_hmac, output_hmac_size());
}
size_t encrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen)
{
if (mode != CRYPTO_API::CipherContext::ENCRYPT)
throw ovpn_tls_crypt_wrong_mode();
return encrypt_decrypt(iv, out, olen, in, ilen);
}
size_t decrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen)
{
if (mode != CRYPTO_API::CipherContext::DECRYPT)
throw ovpn_tls_crypt_wrong_mode();
return encrypt_decrypt(iv, out, olen, in, ilen);
}
private:
// assume length check on header has already been performed
void hmac_pre(const unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len)
{
ctx_hmac.reset();
ctx_hmac.update(header, header_len);
ctx_hmac.update(payload, payload_len);
}
size_t encrypt_decrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen)
{
ctx_crypt.reset(iv);
size_t outlen = 0;
if (!ctx_crypt.update(out, olen, in, ilen, outlen))
return 0;
if (!ctx_crypt.final(out + outlen, olen - outlen, outlen))
return 0;
return outlen;
}
typename CRYPTO_API::HMACContext ctx_hmac;
typename CRYPTO_API::CipherContext ctx_crypt;
int mode;
};
// OvpnHMAC wrapper API using dynamic polymorphism
class TLSCryptInstance : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<TLSCryptInstance> Ptr;
virtual void init(SSLLib::Ctx libctx, const StaticKey &key_hmac, const StaticKey &key_crypt) = 0;
virtual size_t output_hmac_size() const = 0;
virtual bool hmac_gen(unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len) = 0;
virtual bool hmac_cmp(const unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len) = 0;
virtual size_t encrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen) = 0;
virtual size_t decrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen) = 0;
};
class TLSCryptContext : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<TLSCryptContext> Ptr;
virtual size_t digest_size() const = 0;
virtual size_t cipher_key_size() const = 0;
virtual TLSCryptInstance::Ptr new_obj_send() = 0;
virtual TLSCryptInstance::Ptr new_obj_recv() = 0;
// This is the size of the header in a TLSCrypt-wrapped packets,
// excluding the HMAC. Format:
//
// [OP] [PSID] [PID] [HMAC] [...]
//
constexpr const static size_t hmac_offset = 1 + ProtoSessionID::SIZE + PacketID::longidsize;
};
class TLSCryptFactory : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<TLSCryptFactory> Ptr;
virtual TLSCryptContext::Ptr new_obj(SSLLib::Ctx libctx, const CryptoAlgs::Type digest_type, const CryptoAlgs::Type cipher_type) = 0;
};
// TLSCrypt wrapper implementation using dynamic polymorphism
template <typename CRYPTO_API>
class CryptoTLSCryptInstance : public TLSCryptInstance
{
public:
CryptoTLSCryptInstance(SSLLib::Ctx libctx_arg,
const CryptoAlgs::Type digest_arg,
const CryptoAlgs::Type cipher_arg,
int mode_arg)
: digest(digest_arg),
cipher(cipher_arg),
mode(mode_arg),
libctx(libctx_arg)
{
}
void init(SSLLib::Ctx libctx, const StaticKey &key_hmac, const StaticKey &key_crypt)
{
tls_crypt.init(libctx, digest, key_hmac, cipher, key_crypt, mode);
}
size_t output_hmac_size() const
{
return tls_crypt.output_hmac_size();
}
bool hmac_gen(unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len)
{
return tls_crypt.hmac_gen(header, header_len, payload, payload_len);
}
// verify the HMAC generated by hmac_gen, return true if verified
bool hmac_cmp(const unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len)
{
return tls_crypt.hmac_cmp(header, header_len, payload, payload_len);
}
size_t encrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen)
{
return tls_crypt.encrypt(iv, out, olen, in, ilen);
}
size_t decrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen)
{
return tls_crypt.decrypt(iv, out, olen, in, ilen);
}
private:
typename CryptoAlgs::Type digest;
typename CryptoAlgs::Type cipher;
int mode;
TLSCrypt<CRYPTO_API> tls_crypt;
SSLLib::Ctx libctx;
};
template <typename CRYPTO_API>
class CryptoTLSCryptContext : public TLSCryptContext
{
public:
CryptoTLSCryptContext(SSLLib::Ctx libctx_arg, const CryptoAlgs::Type digest_type, const CryptoAlgs::Type cipher_type)
: digest(digest_type),
cipher(cipher_type),
libctx(libctx_arg)
{
}
virtual size_t digest_size() const
{
return CryptoAlgs::size(digest);
}
virtual size_t cipher_key_size() const
{
return CryptoAlgs::key_length(cipher);
}
virtual TLSCryptInstance::Ptr new_obj_send()
{
return new CryptoTLSCryptInstance<CRYPTO_API>(libctx, digest, cipher, CRYPTO_API::CipherContext::ENCRYPT);
}
virtual TLSCryptInstance::Ptr new_obj_recv()
{
return new CryptoTLSCryptInstance<CRYPTO_API>(libctx, digest, cipher, CRYPTO_API::CipherContext::DECRYPT);
}
private:
CryptoAlgs::Type digest;
CryptoAlgs::Type cipher;
SSLLib::Ctx libctx;
};
template <typename CRYPTO_API>
class CryptoTLSCryptFactory : public TLSCryptFactory
{
public:
TLSCryptContext::Ptr new_obj(SSLLib::Ctx libctx, const CryptoAlgs::Type digest_type, const CryptoAlgs::Type cipher_type) override
{
return new CryptoTLSCryptContext<CRYPTO_API>(libctx, digest_type, cipher_type);
}
};
} // namespace openvpn
#endif