OpenVPN 3 Core Library
Loading...
Searching...
No Matches
tls_crypt_v2.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) 2017-2018 OpenVPN Technologies, Inc.
8//
9// This program is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License Version 3
11// as published by the Free Software Foundation.
12//
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program in the COPYING file.
20// If not, see <http://www.gnu.org/licenses/>.
21
22// Classes for handling OpenVPN tls-crypt-v2 internals
23
24#ifndef OPENVPN_CRYPTO_TLS_CRYPT_V2_H
25#define OPENVPN_CRYPTO_TLS_CRYPT_V2_H
26
27#include <cstdint>
28#include <optional>
29#include <string>
30#include <utility>
31
39
40namespace openvpn {
41constexpr static const char *tls_crypt_v2_server_key_name = "OpenVPN tls-crypt-v2 server key";
42constexpr static const char *tls_crypt_v2_client_key_name = "OpenVPN tls-crypt-v2 client key";
43
56inline std::string tls_crypt_v2_pem_to_string(const BufferAllocated &pem)
57{
58 std::string text(reinterpret_cast<const char *>(pem.c_data()), pem.size());
59 while (!text.empty() && text.back() == '\0')
60 text.pop_back();
61 return text;
62}
63
65{
66 public:
67 OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_parse_error);
68 OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_encode_error);
69 OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_bad_size);
70
72 : key_size(128),
73 key(key_size, BufAllocFlags::DESTRUCT_ZERO)
74 {
75 }
76
77 bool defined() const
78 {
79 return key.defined();
80 }
81
82 void parse(const std::string &key_text)
83 {
84 if (!SSLLib::PEMAPI::pem_decode(key, key_text.c_str(), key_text.length(), tls_crypt_v2_server_key_name))
85 throw tls_crypt_v2_server_key_parse_error();
86
87 if (key.size() != key_size)
88 throw tls_crypt_v2_server_key_bad_size();
89 }
90
106
107 void extract_key(OpenVPNStaticKey &tls_key) const
108 {
109 std::memcpy(tls_key.raw_alloc(), key.c_data(), key_size);
110 }
111
112 std::string render() const
113 {
114 BufferAllocated data(32 + 2 * key.size());
115
116 if (!SSLLib::PEMAPI::pem_encode(data, key.c_data(), key.size(), tls_crypt_v2_server_key_name))
117 throw tls_crypt_v2_server_key_encode_error();
118
119 return tls_crypt_v2_pem_to_string(data);
120 }
121
122 private:
123 const size_t key_size;
125};
126
127
129{
130 public:
131 enum
132 {
133 WKC_MAX_SIZE = 1024, // bytes
134 };
135
136 OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_parse_error);
137 OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_encode_error);
138 OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_bad_size);
139
141
143 : key_size(OpenVPNStaticKey::KEY_SIZE),
144 tag_size(context->digest_size()),
145 context_(std::move(context))
146 {
147 }
148
149 bool defined() const
150 {
151 return key.defined() && wkc.defined();
152 }
153
154 void parse(const std::string &key_text)
155 {
157
158 if (!SSLLib::PEMAPI::pem_decode(data, key_text.c_str(), key_text.length(), tls_crypt_v2_client_key_name))
159 throw tls_crypt_v2_client_key_parse_error();
160
161 if (data.size() < (tag_size + key_size))
162 throw tls_crypt_v2_client_key_bad_size();
163
165 wkc.init(data.data() + key_size, data.size() - key_size, BufAllocFlags::DESTRUCT_ZERO);
166 }
167
169 {
170 std::memcpy(tls_key.raw_alloc(), key.c_data(), key_size);
171 }
172
173 std::string render() const
174 {
175 BufferAllocated data(32 + 2 * (key.size() + wkc.size()));
177 in.append(wkc);
178
179 if (!SSLLib::PEMAPI::pem_encode(data, in.c_data(), in.size(), tls_crypt_v2_client_key_name))
180 throw tls_crypt_v2_client_key_encode_error();
181
182 return tls_crypt_v2_pem_to_string(data);
183 }
184
185 void extract_wkc(BufferAllocated &wkc_out) const
186 {
187 wkc_out = wkc;
188 }
189
211 const TLSCryptV2ServerKey &server_key,
212 const std::string &metadata,
213 const int metadata_type,
214 const std::optional<std::uint32_t> key_id,
215 SSLLib::Ctx libctx = nullptr)
216 {
219
220 wkc = wrap(*context_, server_key, key.c_data(), key_size, metadata, metadata_type, key_id, libctx);
221 }
222
257 const TLSCryptV2ServerKey &server_key,
258 const unsigned char *kc,
259 const size_t kc_size,
260 const std::string &metadata,
261 const int metadata_type,
262 const std::optional<std::uint32_t> key_id,
263 SSLLib::Ctx libctx = nullptr)
264 {
265 const size_t hmac_size = context.digest_size();
266
267 OpenVPNStaticKey server_key_material;
268 server_key.extract_key(server_key_material);
269
270 // a single key set, so sliced without direction or mode, as unwrap does
271 TLSCryptInstance::Ptr wrapper = context.new_obj_send();
272 wrapper->init(libctx,
273 server_key_material.slice(OpenVPNStaticKey::HMAC),
274 server_key_material.slice(OpenVPNStaticKey::CIPHER));
275
276 // the encrypted part: Kc, then the metadata behind its type byte
277 BufferAllocated inner(kc_size + 1 + metadata.size(), BufAllocFlags::GROW | BufAllocFlags::DESTRUCT_ZERO);
278 inner.write(kc, kc_size);
279 if (!metadata.empty())
280 {
281 inner.push_back(static_cast<unsigned char>(metadata_type));
282 inner.write(metadata.c_str(), metadata.size());
283 }
284
285 const std::uint32_t k_id_be = htonl(key_id.value_or(0));
286 const size_t k_id_size = key_id ? sizeof(k_id_be) : 0;
287
288 // the trailing length counts itself, the tag, the ciphertext and K_id
289 const size_t wkc_size = sizeof(std::uint16_t) + hmac_size + inner.size() + k_id_size;
290 if (wkc_size > WKC_MAX_SIZE)
291 throw tls_crypt_v2_client_key_bad_size();
292
293 const std::uint16_t wkc_len = static_cast<std::uint16_t>(wkc_size);
294 const std::uint16_t wkc_len_be = htons(wkc_len);
295
296 // the tag covers the length prefix and K_id as well as the plaintext
297 BufferAllocated hmac_input(sizeof(wkc_len_be) + k_id_size + inner.size(),
299 hmac_input.write(&wkc_len_be, sizeof(wkc_len_be));
300 if (key_id)
301 hmac_input.write(&k_id_be, sizeof(k_id_be));
302 hmac_input.write(inner.c_data(), inner.size());
303
305 unsigned char *tag = out.write_alloc(hmac_size);
306 wrapper->hmac_gen(tag, 0, hmac_input.c_data(), hmac_input.size());
307
308 // the tag doubles as the CTR IV, as on the server's decrypt
309 const size_t ciphertext_bytes = wrapper->encrypt(tag,
310 out.data() + hmac_size,
311 out.max_size() - hmac_size,
312 inner.c_data(),
313 inner.size());
314 out.inc_size(ciphertext_bytes);
315 if (key_id)
316 out.write(&k_id_be, sizeof(k_id_be));
317 out.write(&wkc_len_be, sizeof(wkc_len_be));
318
319 return out;
320 }
321
322 private:
325
326 const size_t key_size;
327 const size_t tag_size;
328
331};
332
333// the user can extend the TLSCryptMetadata and the TLSCryptMetadataFactory
334// classes to implement its own metadata verification method.
335//
336// default method is to *ignore* the metadata contained in the WKc sent by the client
337class TLSCryptMetadata : public RC<thread_unsafe_refcount>
338{
339 public:
341
342 // override this method with your own verification mechanism.
343 //
344 // If type is -1 it means that metadata is empty.
345 //
346 virtual bool verify(int type, Buffer &metadata) const
347 {
348 return true;
349 }
350};
351
352// abstract class to be extended when creating other factories
353class TLSCryptMetadataFactory : public RC<thread_unsafe_refcount>
354{
355 public:
357
359};
360
361// factory implementation for the basic verification method
363{
364 public:
366 {
367 return new TLSCryptMetadata();
368 }
369};
370} // namespace openvpn
371
372#endif /* OPENVPN_CRYPTO_TLS_CRYPT_V2_H */
void init(const size_t capacity, const BufferFlags flags=BufAllocFlags::NO_FLAGS)
Initializes the buffer with the specified capacity and flags.
Definition buffer.hpp:1739
bool defined() const
Returns true if the buffer is not empty.
Definition buffer.hpp:1223
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1193
void append(const B &other)
Append data from another buffer to this buffer.
Definition buffer.hpp:1626
void push_back(const T &value)
Append a T object to the end of the array, resizing the array if necessary.
Definition buffer.hpp:1480
const T * c_str() const
Returns a const pointer to the null-terminated string representation of the buffer.
Definition buffer.hpp:1181
T * write_alloc(const size_t size)
Allocate space for writing data to the buffer.
Definition buffer.hpp:1587
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1241
void write(const T *data, const size_t size)
Write data to the buffer.
Definition buffer.hpp:1561
unsigned char * raw_alloc()
StaticKey slice(unsigned int key_specifier) const
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:908
virtual void rand_bytes(unsigned char *buf, size_t size)=0
Fill a buffer with random bytes.
Abstract base class for cryptographically strong random number generators.
Definition randapi.hpp:226
virtual TLSCryptInstance::Ptr new_obj_send()=0
virtual size_t digest_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 size_t encrypt(const unsigned char *iv, unsigned char *out, const size_t olen, const unsigned char *in, const size_t ilen)=0
virtual void init(SSLLib::Ctx libctx, const StaticKey &key_hmac, const StaticKey &key_crypt)=0
virtual TLSCryptMetadata::Ptr new_obj()=0
virtual bool verify(int type, Buffer &metadata) const
void parse(const std::string &key_text)
TLSCryptV2ClientKey(TLSCryptContext::Ptr context)
void extract_wkc(BufferAllocated &wkc_out) const
static BufferAllocated wrap(TLSCryptContext &context, const TLSCryptV2ServerKey &server_key, const unsigned char *kc, const size_t kc_size, const std::string &metadata, const int metadata_type, const std::optional< std::uint32_t > key_id, SSLLib::Ctx libctx=nullptr)
Wrap client key material and metadata into a WKc.
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_bad_size)
void generate(StrongRandomAPI &rng, const TLSCryptV2ServerKey &server_key, const std::string &metadata, const int metadata_type, const std::optional< std::uint32_t > key_id, SSLLib::Ctx libctx=nullptr)
Mint a fresh client key Kc and the WKc that carries it to a server.
void extract_key(OpenVPNStaticKey &tls_key)
TLSCryptContext::Ptr context_
The digest/cipher pair generate() wraps with.
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_parse_error)
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_encode_error)
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_bad_size)
void generate(StrongRandomAPI &rng)
Mint a fresh server key, replacing any key already held.
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_parse_error)
void parse(const std::string &key_text)
void extract_key(OpenVPNStaticKey &tls_key) const
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_encode_error)
constexpr BufferFlags GROW(1U<< 2)
if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
constexpr BufferFlags DESTRUCT_ZERO(1U<< 1)
if enabled, destructor will zero data before deletion
static constexpr const char * tls_crypt_v2_client_key_name
std::string tls_crypt_v2_pem_to_string(const BufferAllocated &pem)
Convert a pem_encode() output buffer into a std::string.
static constexpr const char * tls_crypt_v2_server_key_name
Implementation of the base classes for random number generators.
static std::stringstream out
Definition test_path.cpp:10
const char key_text[]