Browse code

tls-crypt-v2: generate tls-crypt-v2 keys

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>

Steffan Karger authored on 2018/10/22 20:45:11
Showing 9 changed files
... ...
@@ -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, &timestamp, 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