As a first step towards a full tls-crypt-v2 implementation, add
functionality to generate tls-crypt-v2 client and server keys.
Signed-off-by: Steffan Karger <steffan.karger@fox-it.com>
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Message-Id: <1540208715-14044-2-git-send-email-steffan.karger@fox-it.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg17792.html
Signed-off-by: David Sommerseth <davids@openvpn.net>
... | ... |
@@ -5262,6 +5262,57 @@ degrading to the same security as using |
5262 | 5262 |
That is, the control channel still benefits from the extra protection against |
5263 | 5263 |
active man\-in\-the\-middle\-attacks and DoS attacks, but may no longer offer |
5264 | 5264 |
extra privacy and post\-quantum security on top of what TLS itself offers. |
5265 |
+ |
|
5266 |
+For large setups or setups where clients are not trusted, consider using |
|
5267 |
+.B \-\-tls\-crypt\-v2 |
|
5268 |
+instead. That uses per\-client unique keys, and thereby improves the bounds to |
|
5269 |
+\fR'rotate a client key at least once per 8000 years'. |
|
5270 |
+.\"********************************************************* |
|
5271 |
+.TP |
|
5272 |
+.B \-\-tls\-crypt\-v2 keyfile |
|
5273 |
+ |
|
5274 |
+Use client\-specific tls\-crypt keys. |
|
5275 |
+ |
|
5276 |
+For clients, |
|
5277 |
+.B keyfile |
|
5278 |
+is a client\-specific tls\-crypt key. Such a key can be generated using the |
|
5279 |
+.B \-\-tls\-crypt\-v2\-genkey |
|
5280 |
+option. |
|
5281 |
+ |
|
5282 |
+For servers, |
|
5283 |
+.B keyfile |
|
5284 |
+is used to unwrap client\-specific keys supplied by the client during connection |
|
5285 |
+setup. This key must be the same as the key used to generate the |
|
5286 |
+client\-specific key (see |
|
5287 |
+.B \-\-tls\-crypt\-v2\-genkey\fR). |
|
5288 |
+ |
|
5289 |
+On servers, this option can be used together with the |
|
5290 |
+.B \-\-tls\-auth |
|
5291 |
+or |
|
5292 |
+.B \-\-tls\-crypt |
|
5293 |
+option. In that case, the server will detect whether the client is using |
|
5294 |
+client\-specific keys, and automatically select the right mode. |
|
5295 |
+.\"********************************************************* |
|
5296 |
+.TP |
|
5297 |
+.B \-\-tls\-crypt\-v2\-genkey client|server keyfile [metadata] |
|
5298 |
+ |
|
5299 |
+If the first parameter equals "server", generate a \-\-tls\-crypt\-v2 server |
|
5300 |
+key and store the key in |
|
5301 |
+.B keyfile\fR. |
|
5302 |
+ |
|
5303 |
+ |
|
5304 |
+If the first parameter equals "client", generate a \-\-tls\-crypt\-v2 client |
|
5305 |
+key, and store the key in |
|
5306 |
+.B keyfile\fR. |
|
5307 |
+ |
|
5308 |
+If supplied, include the supplied |
|
5309 |
+.B metadata |
|
5310 |
+in the wrapped client key. This metadata must be supplied in base64\-encoded |
|
5311 |
+form. The metadata must be at most 735 bytes long (980 bytes in base64). |
|
5312 |
+ |
|
5313 |
+.B TODO |
|
5314 |
+Metadata handling is not yet implemented. This text will be updated by the |
|
5315 |
+commit that introduces metadata handling. |
|
5265 | 5316 |
.\"********************************************************* |
5266 | 5317 |
.TP |
5267 | 5318 |
.B \-\-askpass [file] |
... | ... |
@@ -34,6 +34,10 @@ |
34 | 34 |
#ifndef _BASE64_H_ |
35 | 35 |
#define _BASE64_H_ |
36 | 36 |
|
37 |
+/** Compute resulting base64 length. 6 bits per byte, padded to 4 bytes. */ |
|
38 |
+#define OPENVPN_BASE64_LENGTH(binary_length) \ |
|
39 |
+ ((((8 * binary_length) / 6) + 3) & ~3) |
|
40 |
+ |
|
37 | 41 |
int openvpn_base64_encode(const void *data, int size, char **str); |
38 | 42 |
|
39 | 43 |
int openvpn_base64_decode(const char *str, void *data, int size); |
... | ... |
@@ -1052,6 +1052,11 @@ print_openssl_info(const struct options *options) |
1052 | 1052 |
bool |
1053 | 1053 |
do_genkey(const struct options *options) |
1054 | 1054 |
{ |
1055 |
+ /* should we disable paging? */ |
|
1056 |
+ if (options->mlock && (options->genkey || options->tls_crypt_v2_genkey_file)) |
|
1057 |
+ { |
|
1058 |
+ platform_mlockall(true); |
|
1059 |
+ } |
|
1055 | 1060 |
if (options->genkey) |
1056 | 1061 |
{ |
1057 | 1062 |
int nbits_written; |
... | ... |
@@ -1059,11 +1064,6 @@ do_genkey(const struct options *options) |
1059 | 1059 |
notnull(options->shared_secret_file, |
1060 | 1060 |
"shared secret output file (--secret)"); |
1061 | 1061 |
|
1062 |
- if (options->mlock) /* should we disable paging? */ |
|
1063 |
- { |
|
1064 |
- platform_mlockall(true); |
|
1065 |
- } |
|
1066 |
- |
|
1067 | 1062 |
nbits_written = write_key_file(2, options->shared_secret_file); |
1068 | 1063 |
if (nbits_written < 0) |
1069 | 1064 |
{ |
... | ... |
@@ -1075,6 +1075,29 @@ do_genkey(const struct options *options) |
1075 | 1075 |
options->shared_secret_file); |
1076 | 1076 |
return true; |
1077 | 1077 |
} |
1078 |
+ if (options->tls_crypt_v2_genkey_type) |
|
1079 |
+ { |
|
1080 |
+ if(!strcmp(options->tls_crypt_v2_genkey_type, "server")) |
|
1081 |
+ { |
|
1082 |
+ tls_crypt_v2_write_server_key_file(options->tls_crypt_v2_genkey_file); |
|
1083 |
+ return true; |
|
1084 |
+ } |
|
1085 |
+ if (options->tls_crypt_v2_genkey_type |
|
1086 |
+ && !strcmp(options->tls_crypt_v2_genkey_type, "client")) |
|
1087 |
+ { |
|
1088 |
+ if (!options->tls_crypt_v2_file) |
|
1089 |
+ { |
|
1090 |
+ msg(M_USAGE, "--tls-crypt-v2-gen-client-key requires a server key to be set via --tls-crypt-v2"); |
|
1091 |
+ } |
|
1092 |
+ |
|
1093 |
+ tls_crypt_v2_write_client_key_file(options->tls_crypt_v2_genkey_file, |
|
1094 |
+ options->tls_crypt_v2_metadata, options->tls_crypt_v2_file, |
|
1095 |
+ options->tls_crypt_v2_inline); |
|
1096 |
+ return true; |
|
1097 |
+ } |
|
1098 |
+ |
|
1099 |
+ msg(M_USAGE, "--tls-crypt-v2-genkey type should be \"client\" or \"server\""); |
|
1100 |
+ } |
|
1078 | 1101 |
return false; |
1079 | 1102 |
} |
1080 | 1103 |
|
... | ... |
@@ -26,6 +26,16 @@ |
26 | 26 |
|
27 | 27 |
#include "error.h" |
28 | 28 |
|
29 |
+#ifndef htonll |
|
30 |
+#define htonll(x) ((1==htonl(1)) ? (x) : \ |
|
31 |
+ ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) |
|
32 |
+#endif |
|
33 |
+ |
|
34 |
+#ifndef ntohll |
|
35 |
+#define ntohll(x) ((1==ntohl(1)) ? (x) : \ |
|
36 |
+ ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) |
|
37 |
+#endif |
|
38 |
+ |
|
29 | 39 |
/* |
30 | 40 |
* min/max functions |
31 | 41 |
*/ |
... | ... |
@@ -622,6 +622,13 @@ static const char usage_message[] = |
622 | 622 |
" attacks on the TLS stack and DoS attacks.\n" |
623 | 623 |
" key (required) provides the pre-shared key file.\n" |
624 | 624 |
" see --secret option for more info.\n" |
625 |
+ "--tls-crypt-v2 key : For clients: use key as a client-specific tls-crypt key.\n" |
|
626 |
+ " For servers: use key to decrypt client-specific keys. For\n" |
|
627 |
+ " key generation (--tls-crypt-v2-genkey): use key to\n" |
|
628 |
+ " encrypt generated client-specific key. (See --tls-crypt.)\n" |
|
629 |
+ "--tls-crypt-v2-genkey client|server keyfile [base64 metadata]: Generate a\n" |
|
630 |
+ " fresh tls-crypt-v2 client or server key, and store to\n" |
|
631 |
+ " keyfile. If supplied, include metadata in wrapped key.\n" |
|
625 | 632 |
"--askpass [file]: Get PEM password from controlling tty before we daemonize.\n" |
626 | 633 |
"--auth-nocache : Don't cache --askpass or --auth-user-pass passwords.\n" |
627 | 634 |
"--crl-verify crl ['dir']: Check peer certificate against a CRL.\n" |
... | ... |
@@ -1449,6 +1456,7 @@ show_connection_entry(const struct connection_entry *o) |
1449 | 1449 |
SHOW_PARM(key_direction, keydirection2ascii(o->key_direction, false, true), |
1450 | 1450 |
"%s"); |
1451 | 1451 |
SHOW_STR(tls_crypt_file); |
1452 |
+ SHOW_STR(tls_crypt_v2_file); |
|
1452 | 1453 |
} |
1453 | 1454 |
|
1454 | 1455 |
|
... | ... |
@@ -1730,6 +1738,10 @@ show_settings(const struct options *o) |
1730 | 1730 |
SHOW_BOOL(push_peer_info); |
1731 | 1731 |
SHOW_BOOL(tls_exit); |
1732 | 1732 |
|
1733 |
+ SHOW_STR(tls_crypt_v2_genkey_type); |
|
1734 |
+ SHOW_STR(tls_crypt_v2_genkey_file); |
|
1735 |
+ SHOW_STR(tls_crypt_v2_metadata); |
|
1736 |
+ |
|
1733 | 1737 |
#ifdef ENABLE_PKCS11 |
1734 | 1738 |
{ |
1735 | 1739 |
int i; |
... | ... |
@@ -2668,6 +2680,15 @@ options_postprocess_verify_ce(const struct options *options, const struct connec |
2668 | 2668 |
{ |
2669 | 2669 |
msg(M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive"); |
2670 | 2670 |
} |
2671 |
+ if (options->tls_client && ce->tls_crypt_v2_file |
|
2672 |
+ && (ce->tls_auth_file || ce->tls_crypt_file)) |
|
2673 |
+ { |
|
2674 |
+ msg(M_USAGE, "--tls-crypt-v2, --tls-auth and --tls-crypt are mutually exclusive in client mode"); |
|
2675 |
+ } |
|
2676 |
+ if (options->genkey && options->tls_crypt_v2_genkey_type) |
|
2677 |
+ { |
|
2678 |
+ msg(M_USAGE, "--genkey and --tls-crypt-v2-genkey are mutually exclusive"); |
|
2679 |
+ } |
|
2671 | 2680 |
} |
2672 | 2681 |
else |
2673 | 2682 |
{ |
... | ... |
@@ -2703,6 +2724,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec |
2703 | 2703 |
MUST_BE_UNDEF(transition_window); |
2704 | 2704 |
MUST_BE_UNDEF(tls_auth_file); |
2705 | 2705 |
MUST_BE_UNDEF(tls_crypt_file); |
2706 |
+ MUST_BE_UNDEF(tls_crypt_v2_file); |
|
2706 | 2707 |
MUST_BE_UNDEF(single_session); |
2707 | 2708 |
MUST_BE_UNDEF(push_peer_info); |
2708 | 2709 |
MUST_BE_UNDEF(tls_exit); |
... | ... |
@@ -2812,12 +2834,12 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) |
2812 | 2812 |
} |
2813 | 2813 |
|
2814 | 2814 |
/* |
2815 |
- * Set per-connection block tls-auth/crypt fields if undefined. |
|
2815 |
+ * Set per-connection block tls-auth/crypt/crypto-v2 fields if undefined. |
|
2816 | 2816 |
* |
2817 |
- * At the end only one of the two will be really set because the parser |
|
2818 |
- * logic prevents configurations where both are set. |
|
2817 |
+ * At the end only one of these will be really set because the parser |
|
2818 |
+ * logic prevents configurations where more are set. |
|
2819 | 2819 |
*/ |
2820 |
- if (!ce->tls_auth_file && !ce->tls_crypt_file) |
|
2820 |
+ if (!ce->tls_auth_file && !ce->tls_crypt_file && !ce->tls_crypt_v2_file) |
|
2821 | 2821 |
{ |
2822 | 2822 |
ce->tls_auth_file = o->tls_auth_file; |
2823 | 2823 |
ce->tls_auth_file_inline = o->tls_auth_file_inline; |
... | ... |
@@ -2825,6 +2847,9 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) |
2825 | 2825 |
|
2826 | 2826 |
ce->tls_crypt_file = o->tls_crypt_file; |
2827 | 2827 |
ce->tls_crypt_inline = o->tls_crypt_inline; |
2828 |
+ |
|
2829 |
+ ce->tls_crypt_v2_file = o->tls_crypt_v2_file; |
|
2830 |
+ ce->tls_crypt_v2_inline = o->tls_crypt_v2_inline; |
|
2828 | 2831 |
} |
2829 | 2832 |
|
2830 | 2833 |
/* pre-cache tls-auth/crypt key file if persist-key was specified and keys |
... | ... |
@@ -3281,9 +3306,15 @@ options_postprocess_filechecks(struct options *options) |
3281 | 3281 |
errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, |
3282 | 3282 |
ce->tls_crypt_file, R_OK, "--tls-crypt"); |
3283 | 3283 |
|
3284 |
+ errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, |
|
3285 |
+ ce->tls_crypt_v2_file, R_OK, |
|
3286 |
+ "--tls-crypt-v2"); |
|
3284 | 3287 |
} |
3285 | 3288 |
|
3286 | 3289 |
errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, |
3290 |
+ options->tls_crypt_v2_genkey_file, R_OK, |
|
3291 |
+ "--tls-crypt-v2-genkey"); |
|
3292 |
+ errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, |
|
3287 | 3293 |
options->shared_secret_file, R_OK, "--secret"); |
3288 | 3294 |
|
3289 | 3295 |
errs |= check_file_access(CHKACC_DIRPATH|CHKACC_FILEXSTWR, |
... | ... |
@@ -8070,6 +8101,36 @@ add_option(struct options *options, |
8070 | 8070 |
|
8071 | 8071 |
} |
8072 | 8072 |
} |
8073 |
+ else if (streq(p[0], "tls-crypt-v2") && p[1] && !p[3]) |
|
8074 |
+ { |
|
8075 |
+ VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION); |
|
8076 |
+ if (permission_mask & OPT_P_GENERAL) |
|
8077 |
+ { |
|
8078 |
+ if (streq(p[1], INLINE_FILE_TAG) && p[2]) |
|
8079 |
+ { |
|
8080 |
+ options->tls_crypt_v2_inline = p[2]; |
|
8081 |
+ } |
|
8082 |
+ options->tls_crypt_v2_file = p[1]; |
|
8083 |
+ } |
|
8084 |
+ else if (permission_mask & OPT_P_CONNECTION) |
|
8085 |
+ { |
|
8086 |
+ if (streq(p[1], INLINE_FILE_TAG) && p[2]) |
|
8087 |
+ { |
|
8088 |
+ options->ce.tls_crypt_v2_inline = p[2]; |
|
8089 |
+ } |
|
8090 |
+ options->ce.tls_crypt_v2_file = p[1]; |
|
8091 |
+ } |
|
8092 |
+ } |
|
8093 |
+ else if (streq(p[0], "tls-crypt-v2-genkey") && p[2] && !p[4]) |
|
8094 |
+ { |
|
8095 |
+ VERIFY_PERMISSION(OPT_P_GENERAL); |
|
8096 |
+ options->tls_crypt_v2_genkey_type = p[1]; |
|
8097 |
+ options->tls_crypt_v2_genkey_file = p[2]; |
|
8098 |
+ if (p[3]) |
|
8099 |
+ { |
|
8100 |
+ options->tls_crypt_v2_metadata = p[3]; |
|
8101 |
+ } |
|
8102 |
+ } |
|
8073 | 8103 |
else if (streq(p[0], "key-method") && p[1] && !p[2]) |
8074 | 8104 |
{ |
8075 | 8105 |
int key_method; |
... | ... |
@@ -139,6 +139,11 @@ struct connection_entry |
139 | 139 |
/* Shared secret used for TLS control channel authenticated encryption */ |
140 | 140 |
const char *tls_crypt_file; |
141 | 141 |
const char *tls_crypt_inline; |
142 |
+ |
|
143 |
+ /* Client-specific secret or server key used for TLS control channel |
|
144 |
+ * authenticated encryption v2 */ |
|
145 |
+ const char *tls_crypt_v2_file; |
|
146 |
+ const char *tls_crypt_v2_inline; |
|
142 | 147 |
}; |
143 | 148 |
|
144 | 149 |
struct remote_entry |
... | ... |
@@ -577,6 +582,15 @@ struct options |
577 | 577 |
const char *tls_crypt_file; |
578 | 578 |
const char *tls_crypt_inline; |
579 | 579 |
|
580 |
+ /* Client-specific secret or server key used for TLS control channel |
|
581 |
+ * authenticated encryption v2 */ |
|
582 |
+ const char *tls_crypt_v2_file; |
|
583 |
+ const char *tls_crypt_v2_inline; |
|
584 |
+ |
|
585 |
+ const char *tls_crypt_v2_genkey_type; |
|
586 |
+ const char *tls_crypt_v2_genkey_file; |
|
587 |
+ const char *tls_crypt_v2_metadata; |
|
588 |
+ |
|
580 | 589 |
/* Allow only one session */ |
581 | 590 |
bool single_session; |
582 | 591 |
|
... | ... |
@@ -29,11 +29,21 @@ |
29 | 29 |
|
30 | 30 |
#include "syshead.h" |
31 | 31 |
|
32 |
+#include "base64.h" |
|
32 | 33 |
#include "crypto.h" |
34 |
+#include "platform.h" |
|
33 | 35 |
#include "session_id.h" |
34 | 36 |
|
35 | 37 |
#include "tls_crypt.h" |
36 | 38 |
|
39 |
+const char *tls_crypt_v2_cli_pem_name = "OpenVPN tls-crypt-v2 client key"; |
|
40 |
+const char *tls_crypt_v2_srv_pem_name = "OpenVPN tls-crypt-v2 server key"; |
|
41 |
+ |
|
42 |
+/** Metadata contains user-specified data */ |
|
43 |
+static const uint8_t TLS_CRYPT_METADATA_TYPE_USER = 0x00; |
|
44 |
+/** Metadata contains a 64-bit unix timestamp in network byte order */ |
|
45 |
+static const uint8_t TLS_CRYPT_METADATA_TYPE_TIMESTAMP = 0x01; |
|
46 |
+ |
|
37 | 47 |
static struct key_type |
38 | 48 |
tls_crypt_kt(void) |
39 | 49 |
{ |
... | ... |
@@ -264,3 +274,283 @@ error_exit: |
264 | 264 |
gc_free(&gc); |
265 | 265 |
return false; |
266 | 266 |
} |
267 |
+ |
|
268 |
+static inline bool |
|
269 |
+tls_crypt_v2_read_keyfile(struct buffer *key, const char *pem_name, |
|
270 |
+ const char *key_file, const char *key_inline) |
|
271 |
+{ |
|
272 |
+ bool ret = false; |
|
273 |
+ struct buffer key_pem = { 0 }; |
|
274 |
+ struct gc_arena gc = gc_new(); |
|
275 |
+ |
|
276 |
+ if (strcmp(key_file, INLINE_FILE_TAG)) |
|
277 |
+ { |
|
278 |
+ key_pem = buffer_read_from_file(key_file, &gc); |
|
279 |
+ if (!buf_valid(&key_pem)) |
|
280 |
+ { |
|
281 |
+ msg(M_WARN, "ERROR: failed to read tls-crypt-v2 key file (%s)", |
|
282 |
+ key_file); |
|
283 |
+ goto cleanup; |
|
284 |
+ } |
|
285 |
+ } |
|
286 |
+ else |
|
287 |
+ { |
|
288 |
+ buf_set_read(&key_pem, (const void *)key_inline, strlen(key_inline)); |
|
289 |
+ } |
|
290 |
+ |
|
291 |
+ if (!crypto_pem_decode(pem_name, key, &key_pem)) |
|
292 |
+ { |
|
293 |
+ msg(M_WARN, "ERROR: tls-crypt-v2 pem decode failed"); |
|
294 |
+ goto cleanup; |
|
295 |
+ } |
|
296 |
+ |
|
297 |
+ ret = true; |
|
298 |
+cleanup: |
|
299 |
+ if (strcmp(key_file, INLINE_FILE_TAG)) |
|
300 |
+ { |
|
301 |
+ buf_clear(&key_pem); |
|
302 |
+ } |
|
303 |
+ gc_free(&gc); |
|
304 |
+ return ret; |
|
305 |
+} |
|
306 |
+ |
|
307 |
+static inline void |
|
308 |
+tls_crypt_v2_load_client_key(struct key_ctx_bi *key, const struct key2 *key2, |
|
309 |
+ bool tls_server) |
|
310 |
+{ |
|
311 |
+ const int key_direction = tls_server ? |
|
312 |
+ KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; |
|
313 |
+ struct key_type kt = tls_crypt_kt(); |
|
314 |
+ if (!kt.cipher || !kt.digest) |
|
315 |
+ { |
|
316 |
+ msg(M_FATAL, "ERROR: --tls-crypt-v2 not supported"); |
|
317 |
+ } |
|
318 |
+ init_key_ctx_bi(key, key2, key_direction, &kt, |
|
319 |
+ "Control Channel Encryption"); |
|
320 |
+} |
|
321 |
+ |
|
322 |
+void |
|
323 |
+tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf, |
|
324 |
+ const char *key_file, const char *key_inline) |
|
325 |
+{ |
|
326 |
+ struct buffer client_key = alloc_buf(TLS_CRYPT_V2_CLIENT_KEY_LEN |
|
327 |
+ + TLS_CRYPT_V2_MAX_WKC_LEN); |
|
328 |
+ |
|
329 |
+ if (!tls_crypt_v2_read_keyfile(&client_key, tls_crypt_v2_cli_pem_name, |
|
330 |
+ key_file, key_inline)) |
|
331 |
+ { |
|
332 |
+ msg(M_FATAL, "ERROR: invalid tls-crypt-v2 client key format"); |
|
333 |
+ } |
|
334 |
+ |
|
335 |
+ struct key2 key2; |
|
336 |
+ if (!buf_read(&client_key, &key2.keys, sizeof(key2.keys))) |
|
337 |
+ { |
|
338 |
+ msg(M_FATAL, "ERROR: not enough data in tls-crypt-v2 client key"); |
|
339 |
+ } |
|
340 |
+ |
|
341 |
+ tls_crypt_v2_load_client_key(key, &key2, false); |
|
342 |
+ secure_memzero(&key2, sizeof(key2)); |
|
343 |
+ |
|
344 |
+ *wkc_buf = client_key; |
|
345 |
+} |
|
346 |
+ |
|
347 |
+void |
|
348 |
+tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt, |
|
349 |
+ const char *key_file, const char *key_inline) |
|
350 |
+{ |
|
351 |
+ struct key srv_key; |
|
352 |
+ struct buffer srv_key_buf; |
|
353 |
+ |
|
354 |
+ buf_set_write(&srv_key_buf, (void *)&srv_key, sizeof(srv_key)); |
|
355 |
+ if (!tls_crypt_v2_read_keyfile(&srv_key_buf, tls_crypt_v2_srv_pem_name, |
|
356 |
+ key_file, key_inline)) |
|
357 |
+ { |
|
358 |
+ msg(M_FATAL, "ERROR: invalid tls-crypt-v2 server key format"); |
|
359 |
+ } |
|
360 |
+ |
|
361 |
+ struct key_type kt = tls_crypt_kt(); |
|
362 |
+ if (!kt.cipher || !kt.digest) |
|
363 |
+ { |
|
364 |
+ msg(M_FATAL, "ERROR: --tls-crypt-v2 not supported"); |
|
365 |
+ } |
|
366 |
+ init_key_ctx(key_ctx, &srv_key, &kt, encrypt, "tls-crypt-v2 server key"); |
|
367 |
+ secure_memzero(&srv_key, sizeof(srv_key)); |
|
368 |
+} |
|
369 |
+ |
|
370 |
+static bool |
|
371 |
+tls_crypt_v2_wrap_client_key(struct buffer *wkc, |
|
372 |
+ const struct key2 *src_key, |
|
373 |
+ const struct buffer *src_metadata, |
|
374 |
+ struct key_ctx *server_key, struct gc_arena *gc) |
|
375 |
+{ |
|
376 |
+ cipher_ctx_t *cipher_ctx = server_key->cipher; |
|
377 |
+ struct buffer work = alloc_buf_gc(TLS_CRYPT_V2_MAX_WKC_LEN |
|
378 |
+ + cipher_ctx_block_size(cipher_ctx), gc); |
|
379 |
+ |
|
380 |
+ /* Calculate auth tag and synthetic IV */ |
|
381 |
+ uint8_t *tag = buf_write_alloc(&work, TLS_CRYPT_TAG_SIZE); |
|
382 |
+ if (!tag) |
|
383 |
+ { |
|
384 |
+ msg(M_WARN, "ERROR: could not write tag"); |
|
385 |
+ return false; |
|
386 |
+ } |
|
387 |
+ uint16_t net_len = htons(sizeof(src_key->keys) + BLEN(src_metadata) |
|
388 |
+ + TLS_CRYPT_V2_TAG_SIZE + sizeof(uint16_t)); |
|
389 |
+ hmac_ctx_t *hmac_ctx = server_key->hmac; |
|
390 |
+ hmac_ctx_reset(hmac_ctx); |
|
391 |
+ hmac_ctx_update(hmac_ctx, (void *)&net_len, sizeof(net_len)); |
|
392 |
+ hmac_ctx_update(hmac_ctx, (void *)src_key->keys, sizeof(src_key->keys)); |
|
393 |
+ hmac_ctx_update(hmac_ctx, BPTR(src_metadata), BLEN(src_metadata)); |
|
394 |
+ hmac_ctx_final(hmac_ctx, tag); |
|
395 |
+ |
|
396 |
+ dmsg(D_CRYPTO_DEBUG, "TLS-CRYPT WRAP TAG: %s", |
|
397 |
+ format_hex(tag, TLS_CRYPT_TAG_SIZE, 0, gc)); |
|
398 |
+ |
|
399 |
+ /* Use the 128 most significant bits of the tag as IV */ |
|
400 |
+ ASSERT(cipher_ctx_reset(cipher_ctx, tag)); |
|
401 |
+ |
|
402 |
+ /* Overflow check (OpenSSL requires an extra block in the dst buffer) */ |
|
403 |
+ if (buf_forward_capacity(&work) < (sizeof(src_key->keys) |
|
404 |
+ + BLEN(src_metadata) |
|
405 |
+ + sizeof(net_len) |
|
406 |
+ + cipher_ctx_block_size(cipher_ctx))) |
|
407 |
+ { |
|
408 |
+ msg(M_WARN, "ERROR: could not crypt: insufficient space in dst"); |
|
409 |
+ return false; |
|
410 |
+ } |
|
411 |
+ |
|
412 |
+ /* Encrypt */ |
|
413 |
+ int outlen = 0; |
|
414 |
+ ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen, |
|
415 |
+ (void *)src_key->keys, sizeof(src_key->keys))); |
|
416 |
+ ASSERT(buf_inc_len(&work, outlen)); |
|
417 |
+ ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen, |
|
418 |
+ BPTR(src_metadata), BLEN(src_metadata))); |
|
419 |
+ ASSERT(buf_inc_len(&work, outlen)); |
|
420 |
+ ASSERT(cipher_ctx_final(cipher_ctx, BEND(&work), &outlen)); |
|
421 |
+ ASSERT(buf_inc_len(&work, outlen)); |
|
422 |
+ ASSERT(buf_write(&work, &net_len, sizeof(net_len))); |
|
423 |
+ |
|
424 |
+ return buf_copy(wkc, &work); |
|
425 |
+} |
|
426 |
+ |
|
427 |
+void |
|
428 |
+tls_crypt_v2_write_server_key_file(const char *filename) |
|
429 |
+{ |
|
430 |
+ struct gc_arena gc = gc_new(); |
|
431 |
+ struct key server_key = { 0 }; |
|
432 |
+ struct buffer server_key_buf = clear_buf(); |
|
433 |
+ struct buffer server_key_pem = clear_buf(); |
|
434 |
+ |
|
435 |
+ if (!rand_bytes((void *)&server_key, sizeof(server_key))) |
|
436 |
+ { |
|
437 |
+ msg(M_NONFATAL, "ERROR: could not generate random key"); |
|
438 |
+ goto cleanup; |
|
439 |
+ } |
|
440 |
+ buf_set_read(&server_key_buf, (void *)&server_key, sizeof(server_key)); |
|
441 |
+ if (!crypto_pem_encode(tls_crypt_v2_srv_pem_name, &server_key_pem, |
|
442 |
+ &server_key_buf, &gc)) |
|
443 |
+ { |
|
444 |
+ msg(M_WARN, "ERROR: could not PEM-encode server key"); |
|
445 |
+ goto cleanup; |
|
446 |
+ } |
|
447 |
+ |
|
448 |
+ if (!buffer_write_file(filename, &server_key_pem)) |
|
449 |
+ { |
|
450 |
+ msg(M_ERR, "ERROR: could not write server key file"); |
|
451 |
+ goto cleanup; |
|
452 |
+ } |
|
453 |
+ |
|
454 |
+cleanup: |
|
455 |
+ secure_memzero(&server_key, sizeof(server_key)); |
|
456 |
+ buf_clear(&server_key_pem); |
|
457 |
+ gc_free(&gc); |
|
458 |
+ return; |
|
459 |
+} |
|
460 |
+ |
|
461 |
+void |
|
462 |
+tls_crypt_v2_write_client_key_file(const char *filename, |
|
463 |
+ const char *b64_metadata, |
|
464 |
+ const char *server_key_file, |
|
465 |
+ const char *server_key_inline) |
|
466 |
+{ |
|
467 |
+ struct gc_arena gc = gc_new(); |
|
468 |
+ struct key_ctx server_key = { 0 }; |
|
469 |
+ struct buffer client_key_pem = { 0 }; |
|
470 |
+ struct buffer dst = alloc_buf_gc(TLS_CRYPT_V2_CLIENT_KEY_LEN |
|
471 |
+ + TLS_CRYPT_V2_MAX_WKC_LEN, &gc); |
|
472 |
+ struct key2 client_key = { 2 }; |
|
473 |
+ |
|
474 |
+ if (!rand_bytes((void *)client_key.keys, sizeof(client_key.keys))) |
|
475 |
+ { |
|
476 |
+ msg(M_FATAL, "ERROR: could not generate random key"); |
|
477 |
+ goto cleanup; |
|
478 |
+ } |
|
479 |
+ ASSERT(buf_write(&dst, client_key.keys, sizeof(client_key.keys))); |
|
480 |
+ |
|
481 |
+ struct buffer metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN, &gc); |
|
482 |
+ if (b64_metadata) |
|
483 |
+ { |
|
484 |
+ if (TLS_CRYPT_V2_MAX_B64_METADATA_LEN < strlen(b64_metadata)) |
|
485 |
+ { |
|
486 |
+ msg(M_FATAL, |
|
487 |
+ "ERROR: metadata too long (%d bytes, max %u bytes)", |
|
488 |
+ (int)strlen(b64_metadata), TLS_CRYPT_V2_MAX_B64_METADATA_LEN); |
|
489 |
+ } |
|
490 |
+ ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_USER, 1)); |
|
491 |
+ int decoded_len = openvpn_base64_decode(b64_metadata, BPTR(&metadata), |
|
492 |
+ BCAP(&metadata)); |
|
493 |
+ if (decoded_len < 0) |
|
494 |
+ { |
|
495 |
+ msg(M_FATAL, "ERROR: failed to base64 decode provided metadata"); |
|
496 |
+ goto cleanup; |
|
497 |
+ } |
|
498 |
+ ASSERT(buf_inc_len(&metadata, decoded_len)); |
|
499 |
+ } |
|
500 |
+ else |
|
501 |
+ { |
|
502 |
+ int64_t timestamp = htonll(now); |
|
503 |
+ ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_TIMESTAMP, 1)); |
|
504 |
+ ASSERT(buf_write(&metadata, ×tamp, sizeof(timestamp))); |
|
505 |
+ } |
|
506 |
+ |
|
507 |
+ tls_crypt_v2_init_server_key(&server_key, true, server_key_file, |
|
508 |
+ server_key_inline); |
|
509 |
+ if (!tls_crypt_v2_wrap_client_key(&dst, &client_key, &metadata, &server_key, |
|
510 |
+ &gc)) |
|
511 |
+ { |
|
512 |
+ msg(M_FATAL, "ERROR: could not wrap generated client key"); |
|
513 |
+ goto cleanup; |
|
514 |
+ } |
|
515 |
+ |
|
516 |
+ /* PEM-encode Kc || WKc */ |
|
517 |
+ if (!crypto_pem_encode(tls_crypt_v2_cli_pem_name, &client_key_pem, &dst, |
|
518 |
+ &gc)) |
|
519 |
+ { |
|
520 |
+ msg(M_FATAL, "ERROR: could not PEM-encode client key"); |
|
521 |
+ goto cleanup; |
|
522 |
+ } |
|
523 |
+ |
|
524 |
+ if (!buffer_write_file(filename, &client_key_pem)) |
|
525 |
+ { |
|
526 |
+ msg(M_FATAL, "ERROR: could not write client key file"); |
|
527 |
+ goto cleanup; |
|
528 |
+ } |
|
529 |
+ |
|
530 |
+ /* Sanity check: load client key (as "client") */ |
|
531 |
+ struct key_ctx_bi test_client_key; |
|
532 |
+ struct buffer test_wrapped_client_key; |
|
533 |
+ msg(D_GENKEY, "Testing client-side key loading..."); |
|
534 |
+ tls_crypt_v2_init_client_key(&test_client_key, &test_wrapped_client_key, |
|
535 |
+ filename, NULL); |
|
536 |
+ free_key_ctx_bi(&test_client_key); |
|
537 |
+ free_buf(&test_wrapped_client_key); |
|
538 |
+ |
|
539 |
+cleanup: |
|
540 |
+ secure_memzero(&client_key, sizeof(client_key)); |
|
541 |
+ free_key_ctx(&server_key); |
|
542 |
+ buf_clear(&client_key_pem); |
|
543 |
+ buf_clear(&dst); |
|
544 |
+ |
|
545 |
+ gc_free(&gc); |
|
546 |
+} |
... | ... |
@@ -22,15 +22,13 @@ |
22 | 22 |
*/ |
23 | 23 |
|
24 | 24 |
/** |
25 |
- * @defgroup tls_crypt Control channel encryption (--tls-crypt) |
|
25 |
+ * @defgroup tls_crypt Control channel encryption (--tls-crypt, --tls-crypt-v2) |
|
26 | 26 |
* @ingroup control_tls |
27 | 27 |
* @{ |
28 | 28 |
* |
29 |
- * @par |
|
30 | 29 |
* Control channel encryption uses a pre-shared static key (like the --tls-auth |
31 | 30 |
* key) to encrypt control channel packets. |
32 | 31 |
* |
33 |
- * @par |
|
34 | 32 |
* Encrypting control channel packets has three main advantages: |
35 | 33 |
* - It provides more privacy by hiding the certificate used for the TLS |
36 | 34 |
* connection. |
... | ... |
@@ -38,11 +36,20 @@ |
38 | 38 |
* - It provides "poor-man's" post-quantum security, against attackers who |
39 | 39 |
* will never know the pre-shared key (i.e. no forward secrecy). |
40 | 40 |
* |
41 |
- * @par Specification |
|
41 |
+ * --tls-crypt uses a tls-auth-style group key, where all servers and clients |
|
42 |
+ * share the same group key. --tls-crypt-v2 adds support for client-specific |
|
43 |
+ * keys, where all servers share the same client-key encryption key, and each |
|
44 |
+ * clients receives a unique client key, both in plaintext and in encrypted |
|
45 |
+ * form. When connecting to a server, the client sends the encrypted key to |
|
46 |
+ * the server in the first packet (P_CONTROL_HARD_RESET_CLIENT_V3). The server |
|
47 |
+ * then decrypts that key, and both parties can use the same client-specific |
|
48 |
+ * key for tls-crypt packets. See doc/tls-crypt-v2.txt for more details. |
|
49 |
+ * |
|
50 |
+ * @par On-the-wire tls-crypt packet specification |
|
51 |
+ * @parblock |
|
42 | 52 |
* Control channel encryption is based on the SIV construction [0], to achieve |
43 | 53 |
* nonce misuse-resistant authenticated encryption: |
44 | 54 |
* |
45 |
- * @par |
|
46 | 55 |
* \code{.unparsed} |
47 | 56 |
* msg = control channel plaintext |
48 | 57 |
* header = opcode (1 byte) || session_id (8 bytes) || packet_id (8 bytes) |
... | ... |
@@ -57,23 +64,23 @@ |
57 | 57 |
* output = Header || Tag || Ciph |
58 | 58 |
* \endcode |
59 | 59 |
* |
60 |
- * @par |
|
61 | 60 |
* This boils down to the following on-the-wire packet format: |
62 | 61 |
* |
63 |
- * @par |
|
64 | 62 |
* \code{.unparsed} |
65 | 63 |
* - opcode - || - session_id - || - packet_id - || auth_tag || * payload * |
66 | 64 |
* \endcode |
67 | 65 |
* |
68 |
- * @par |
|
69 | 66 |
* Where |
70 | 67 |
* <tt>- XXX -</tt> means authenticated, and |
71 | 68 |
* <tt>* XXX *</tt> means authenticated and encrypted. |
69 |
+ * |
|
70 |
+ * @endparblock |
|
72 | 71 |
*/ |
73 | 72 |
|
74 | 73 |
#ifndef TLSCRYPT_H |
75 | 74 |
#define TLSCRYPT_H |
76 | 75 |
|
76 |
+#include "base64.h" |
|
77 | 77 |
#include "buffer.h" |
78 | 78 |
#include "crypto.h" |
79 | 79 |
#include "session_id.h" |
... | ... |
@@ -86,6 +93,16 @@ |
86 | 86 |
#define TLS_CRYPT_OFF_TAG (TLS_CRYPT_OFF_PID + TLS_CRYPT_PID_SIZE) |
87 | 87 |
#define TLS_CRYPT_OFF_CT (TLS_CRYPT_OFF_TAG + TLS_CRYPT_TAG_SIZE) |
88 | 88 |
|
89 |
+#define TLS_CRYPT_V2_MAX_WKC_LEN (1024) |
|
90 |
+#define TLS_CRYPT_V2_CLIENT_KEY_LEN (2048 / 8) |
|
91 |
+#define TLS_CRYPT_V2_SERVER_KEY_LEN (sizeof(struct key)) |
|
92 |
+#define TLS_CRYPT_V2_TAG_SIZE (TLS_CRYPT_TAG_SIZE) |
|
93 |
+#define TLS_CRYPT_V2_MAX_METADATA_LEN (unsigned)(TLS_CRYPT_V2_MAX_WKC_LEN \ |
|
94 |
+ - (TLS_CRYPT_V2_CLIENT_KEY_LEN + TLS_CRYPT_V2_TAG_SIZE \ |
|
95 |
+ + sizeof(uint16_t))) |
|
96 |
+#define TLS_CRYPT_V2_MAX_B64_METADATA_LEN \ |
|
97 |
+ OPENVPN_BASE64_LENGTH(TLS_CRYPT_V2_MAX_METADATA_LEN - 1) |
|
98 |
+ |
|
89 | 99 |
/** |
90 | 100 |
* Initialize a key_ctx_bi structure for use with --tls-crypt. |
91 | 101 |
* |
... | ... |
@@ -138,6 +155,56 @@ bool tls_crypt_wrap(const struct buffer *src, struct buffer *dst, |
138 | 138 |
bool tls_crypt_unwrap(const struct buffer *src, struct buffer *dst, |
139 | 139 |
struct crypto_options *opt); |
140 | 140 |
|
141 |
+/** |
|
142 |
+ * Initialize a tls-crypt-v2 server key (used to encrypt/decrypt client keys). |
|
143 |
+ * |
|
144 |
+ * @param key Key structure to be initialized. Must be non-NULL. |
|
145 |
+ * @parem encrypt If true, initialize the key structure for encryption, |
|
146 |
+ * otherwise for decryption. |
|
147 |
+ * @param key_file File path of the key file to load, or INLINE tag. |
|
148 |
+ * @param key_inline Inline key file contents (or NULL if not inline). |
|
149 |
+ */ |
|
150 |
+void tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt, |
|
151 |
+ const char *key_file, const char *key_inline); |
|
152 |
+ |
|
153 |
+/** |
|
154 |
+ * Initialize a tls-crypt-v2 client key. |
|
155 |
+ * |
|
156 |
+ * @param key Key structure to be initialized with the client |
|
157 |
+ * key. |
|
158 |
+ * @param wrapped_key_buf Returns buffer containing the wrapped key that will |
|
159 |
+ * be sent to the server when connecting. Caller must |
|
160 |
+ * free this buffer when no longer needed. |
|
161 |
+ * @param key_file File path of the key file to load, or INLINE tag. |
|
162 |
+ * @param key_inline Inline key file contents (or NULL if not inline). |
|
163 |
+ */ |
|
164 |
+void tls_crypt_v2_init_client_key(struct key_ctx_bi *key, |
|
165 |
+ struct buffer *wrapped_key_buf, |
|
166 |
+ const char *key_file, |
|
167 |
+ const char *key_inline); |
|
168 |
+ |
|
169 |
+/** |
|
170 |
+ * Generate a tls-crypt-v2 server key, and write to file. |
|
171 |
+ * |
|
172 |
+ * @param filename Filename of the server key file to create. |
|
173 |
+ */ |
|
174 |
+void tls_crypt_v2_write_server_key_file(const char *filename); |
|
175 |
+ |
|
176 |
+/** |
|
177 |
+ * Generate a tls-crypt-v2 client key, and write to file. |
|
178 |
+ * |
|
179 |
+ * @param filename Filename of the client key file to create. |
|
180 |
+ * @param b64_metadata Base64 metadata to be included in the client key. |
|
181 |
+ * @param server_key_file File path of the server key to use for wrapping the |
|
182 |
+ * client key, or INLINE tag. |
|
183 |
+ * @param server_key_inline Inline server key file contents (or NULL if not |
|
184 |
+ * inline). |
|
185 |
+ */ |
|
186 |
+void tls_crypt_v2_write_client_key_file(const char *filename, |
|
187 |
+ const char *b64_metadata, |
|
188 |
+ const char *key_file, |
|
189 |
+ const char *key_inline); |
|
190 |
+ |
|
141 | 191 |
/** @} */ |
142 | 192 |
|
143 | 193 |
#endif /* TLSCRYPT_H */ |
... | ... |
@@ -21,8 +21,8 @@ |
21 | 21 |
|
22 | 22 |
set -eu |
23 | 23 |
top_builddir="${top_builddir:-..}" |
24 |
-trap "rm -f key.$$ log.$$ ; trap 0 ; exit 77" 1 2 15 |
|
25 |
-trap "rm -f key.$$ log.$$ ; exit 1" 0 3 |
|
24 |
+trap "rm -f key.$$ tc-server-key.$$ tc-client-key.$$ log.$$ ; trap 0 ; exit 77" 1 2 15 |
|
25 |
+trap "rm -f key.$$ tc-server-key.$$ tc-client-key.$$ log.$$ ; exit 1" 0 3 |
|
26 | 26 |
|
27 | 27 |
# Get list of supported ciphers from openvpn --show-ciphers output |
28 | 28 |
CIPHERS=$(${top_builddir}/src/openvpn/openvpn --show-ciphers | \ |
... | ... |
@@ -55,6 +55,40 @@ do |
55 | 55 |
fi |
56 | 56 |
done |
57 | 57 |
|
58 |
-rm key.$$ log.$$ |
|
58 |
+echo -n "Testing tls-crypt-v2 server key generation..." |
|
59 |
+"${top_builddir}/src/openvpn/openvpn" \ |
|
60 |
+ --tls-crypt-v2-genkey server tc-server-key.$$ >log.$$ 2>&1 |
|
61 |
+if [ $? != 0 ] ; then |
|
62 |
+ echo "FAILED" |
|
63 |
+ cat log.$$ |
|
64 |
+ e=1 |
|
65 |
+else |
|
66 |
+ echo "OK" |
|
67 |
+fi |
|
68 |
+ |
|
69 |
+echo -n "Testing tls-crypt-v2 key generation (no metadata)..." |
|
70 |
+"${top_builddir}/src/openvpn/openvpn" --tls-crypt-v2 tc-server-key.$$ \ |
|
71 |
+ --tls-crypt-v2-genkey client tc-client-key.$$ >log.$$ 2>&1 |
|
72 |
+if [ $? != 0 ] ; then |
|
73 |
+ echo "FAILED" |
|
74 |
+ cat log.$$ |
|
75 |
+ e=1 |
|
76 |
+else |
|
77 |
+ echo "OK" |
|
78 |
+fi |
|
79 |
+ |
|
80 |
+echo -n "Testing tls-crypt-v2 key generation (max length metadata)..." |
|
81 |
+"${top_builddir}/src/openvpn/openvpn" --tls-crypt-v2 tc-server-key.$$ \ |
|
82 |
+ --tls-crypt-v2-genkey client tc-client-key.$$ \ |
|
83 |
+ $(head -c732 /dev/zero | base64 -w0) >log.$$ 2>&1 |
|
84 |
+if [ $? != 0 ] ; then |
|
85 |
+ echo "FAILED" |
|
86 |
+ cat log.$$ |
|
87 |
+ e=1 |
|
88 |
+else |
|
89 |
+ echo "OK" |
|
90 |
+fi |
|
91 |
+ |
|
92 |
+rm key.$$ tc-server-key.$$ tc-client-key.$$ log.$$ |
|
59 | 93 |
trap 0 |
60 | 94 |
exit $e |