src/openvpn/tls_crypt.c
c6e24fa3
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
c6e24fa3
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  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 General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
c6e24fa3
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
 #include "syshead.h"
 
9d59029a
 #include "base64.h"
c6e24fa3
 #include "crypto.h"
9d59029a
 #include "platform.h"
c6e24fa3
 #include "session_id.h"
19dffdbd
 #include "ssl.h"
c6e24fa3
 
 #include "tls_crypt.h"
 
9d59029a
 const char *tls_crypt_v2_cli_pem_name = "OpenVPN tls-crypt-v2 client key";
 const char *tls_crypt_v2_srv_pem_name = "OpenVPN tls-crypt-v2 server key";
 
 /** Metadata contains user-specified data */
 static const uint8_t TLS_CRYPT_METADATA_TYPE_USER           = 0x00;
 /** Metadata contains a 64-bit unix timestamp in network byte order */
 static const uint8_t TLS_CRYPT_METADATA_TYPE_TIMESTAMP      = 0x01;
 
489c7bf9
 static struct key_type
 tls_crypt_kt(void)
4cd4899e
 {
81d882d5
     struct key_type kt;
     kt.cipher = cipher_kt_get("AES-256-CTR");
     kt.digest = md_kt_get("SHA256");
 
     if (!kt.cipher)
c6e24fa3
     {
489c7bf9
         msg(M_WARN, "ERROR: --tls-crypt requires AES-256-CTR support.");
         return (struct key_type) { 0 };
c6e24fa3
     }
81d882d5
     if (!kt.digest)
c6e24fa3
     {
489c7bf9
         msg(M_WARN, "ERROR: --tls-crypt requires HMAC-SHA-256 support.");
         return (struct key_type) { 0 };
c6e24fa3
     }
 
2fe5547c
     kt.cipher_length = cipher_kt_key_size(kt.cipher);
     kt.hmac_length = md_kt_size(kt.digest);
 
489c7bf9
     return kt;
 }
 
 int
 tls_crypt_buf_overhead(void)
 {
     return packet_id_size(true) + TLS_CRYPT_TAG_SIZE + TLS_CRYPT_BLOCK_SIZE;
 }
 
 void
 tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file,
                    const char *key_inline, bool tls_server)
 {
     const int key_direction = tls_server ?
                               KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE;
     struct key_type kt = tls_crypt_kt();
     if (!kt.cipher || !kt.digest)
     {
         msg (M_FATAL, "ERROR: --tls-crypt not supported");
     }
81d882d5
     crypto_read_openvpn_key(&kt, key, key_file, key_inline, key_direction,
                             "Control Channel Encryption", "tls-crypt");
c6e24fa3
 }
 
 void
 tls_crypt_adjust_frame_parameters(struct frame *frame)
 {
81d882d5
     frame_add_to_extra_frame(frame, tls_crypt_buf_overhead());
c6e24fa3
 
81d882d5
     msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for tls-crypt by %i bytes",
         __func__, tls_crypt_buf_overhead());
c6e24fa3
 }
 
 
 bool
81d882d5
 tls_crypt_wrap(const struct buffer *src, struct buffer *dst,
4cd4899e
                struct crypto_options *opt)
 {
81d882d5
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     struct gc_arena gc;
 
     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
     ASSERT(ctx->hmac);
     ASSERT(packet_id_initialized(&opt->packet_id));
     ASSERT(hmac_ctx_size(ctx->hmac) == 256/8);
 
     gc_init(&gc);
 
     dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP FROM: %s",
          format_hex(BPTR(src), BLEN(src), 80, &gc));
 
     /* Get packet ID */
e498cb0e
     if (!packet_id_write(&opt->packet_id.send, dst, true, false))
     {
         msg(D_CRYPT_ERRORS, "TLS-CRYPT ERROR: packet ID roll over.");
         goto err;
     }
c6e24fa3
 
81d882d5
     dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP AD: %s",
          format_hex(BPTR(dst), BLEN(dst), 0, &gc));
 
     /* Buffer overflow check */
     if (!buf_safe(dst, BLEN(src) + TLS_CRYPT_BLOCK_SIZE + TLS_CRYPT_TAG_SIZE))
     {
         msg(D_CRYPT_ERRORS, "TLS-CRYPT WRAP: buffer size error, "
             "sc=%d so=%d sl=%d dc=%d do=%d dl=%d", src->capacity, src->offset,
             src->len, dst->capacity, dst->offset, dst->len);
         goto err;
     }
 
     /* Calculate auth tag and synthetic IV */
     {
         uint8_t *tag = NULL;
         hmac_ctx_reset(ctx->hmac);
         hmac_ctx_update(ctx->hmac, BPTR(dst), BLEN(dst));
         hmac_ctx_update(ctx->hmac, BPTR(src), BLEN(src));
c6e24fa3
 
81d882d5
         ASSERT(tag = buf_write_alloc(dst, TLS_CRYPT_TAG_SIZE));
         hmac_ctx_final(ctx->hmac, tag);
c6e24fa3
 
81d882d5
         dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP TAG: %s",
              format_hex(tag, TLS_CRYPT_TAG_SIZE, 0, &gc));
c6e24fa3
 
81d882d5
         /* Use the 128 most significant bits of the tag as IV */
         ASSERT(cipher_ctx_reset(ctx->cipher, tag));
     }
c6e24fa3
 
81d882d5
     /* Encrypt src */
     {
         int outlen = 0;
         ASSERT(cipher_ctx_update(ctx->cipher, BEND(dst), &outlen,
                                  BPTR(src), BLEN(src)));
         ASSERT(buf_inc_len(dst, outlen));
         ASSERT(cipher_ctx_final(ctx->cipher, BPTR(dst), &outlen));
         ASSERT(buf_inc_len(dst, outlen));
     }
c6e24fa3
 
81d882d5
     dmsg(D_PACKET_CONTENT, "TLS-CRYPT WRAP TO: %s",
          format_hex(BPTR(dst), BLEN(dst), 80, &gc));
c6e24fa3
 
81d882d5
     gc_free(&gc);
     return true;
c6e24fa3
 
 err:
81d882d5
     crypto_clear_error();
     dst->len = 0;
     gc_free(&gc);
     return false;
c6e24fa3
 }
 
 bool
81d882d5
 tls_crypt_unwrap(const struct buffer *src, struct buffer *dst,
                  struct crypto_options *opt)
c6e24fa3
 {
81d882d5
     static const char error_prefix[] = "tls-crypt unwrap error";
     const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt;
     struct gc_arena gc;
c6e24fa3
 
81d882d5
     gc_init(&gc);
c6e24fa3
 
81d882d5
     ASSERT(opt);
     ASSERT(src->len > 0);
     ASSERT(ctx->cipher);
     ASSERT(packet_id_initialized(&opt->packet_id)
            || (opt->flags & CO_IGNORE_PACKET_ID));
c6e24fa3
 
81d882d5
     dmsg(D_PACKET_CONTENT, "TLS-CRYPT UNWRAP FROM: %s",
          format_hex(BPTR(src), BLEN(src), 80, &gc));
 
     if (buf_len(src) < TLS_CRYPT_OFF_CT)
     {
         CRYPT_ERROR("packet too short");
     }
 
     /* Decrypt cipher text */
     {
         int outlen = 0;
 
         /* Buffer overflow check (should never fail) */
         if (!buf_safe(dst, BLEN(src) - TLS_CRYPT_OFF_CT + TLS_CRYPT_BLOCK_SIZE))
         {
             CRYPT_ERROR("potential buffer overflow");
         }
 
         if (!cipher_ctx_reset(ctx->cipher, BPTR(src) + TLS_CRYPT_OFF_TAG))
         {
             CRYPT_ERROR("cipher reset failed");
         }
         if (!cipher_ctx_update(ctx->cipher, BPTR(dst), &outlen,
                                BPTR(src) + TLS_CRYPT_OFF_CT, BLEN(src) - TLS_CRYPT_OFF_CT))
         {
             CRYPT_ERROR("cipher update failed");
         }
         ASSERT(buf_inc_len(dst, outlen));
         if (!cipher_ctx_final(ctx->cipher, BPTR(dst), &outlen))
         {
             CRYPT_ERROR("cipher final failed");
         }
         ASSERT(buf_inc_len(dst, outlen));
     }
c6e24fa3
 
81d882d5
     /* Check authentication */
c6e24fa3
     {
81d882d5
         const uint8_t *tag = BPTR(src) + TLS_CRYPT_OFF_TAG;
         uint8_t tag_check[TLS_CRYPT_TAG_SIZE] = { 0 };
 
         dmsg(D_PACKET_CONTENT, "TLS-CRYPT UNWRAP AD: %s",
              format_hex(BPTR(src), TLS_CRYPT_OFF_TAG, 0, &gc));
         dmsg(D_PACKET_CONTENT, "TLS-CRYPT UNWRAP TO: %s",
              format_hex(BPTR(dst), BLEN(dst), 80, &gc));
 
         hmac_ctx_reset(ctx->hmac);
         hmac_ctx_update(ctx->hmac, BPTR(src), TLS_CRYPT_OFF_TAG);
         hmac_ctx_update(ctx->hmac, BPTR(dst), BLEN(dst));
         hmac_ctx_final(ctx->hmac, tag_check);
 
         if (memcmp_constant_time(tag, tag_check, sizeof(tag_check)))
         {
             dmsg(D_CRYPTO_DEBUG, "tag      : %s",
                  format_hex(tag, sizeof(tag_check), 0, &gc));
             dmsg(D_CRYPTO_DEBUG, "tag_check: %s",
                  format_hex(tag_check, sizeof(tag_check), 0, &gc));
             CRYPT_ERROR("packet authentication failed");
         }
c6e24fa3
     }
 
81d882d5
     /* Check replay */
     if (!(opt->flags & CO_IGNORE_PACKET_ID))
c6e24fa3
     {
81d882d5
         struct packet_id_net pin;
         struct buffer tmp = *src;
         ASSERT(buf_advance(&tmp, TLS_CRYPT_OFF_PID));
         ASSERT(packet_id_read(&pin, &tmp, true));
         if (!crypto_check_replay(opt, &pin, error_prefix, &gc))
         {
             CRYPT_ERROR("packet replay");
         }
c6e24fa3
     }
 
81d882d5
     gc_free(&gc);
     return true;
c6e24fa3
 
81d882d5
 error_exit:
     crypto_clear_error();
     dst->len = 0;
     gc_free(&gc);
     return false;
c6e24fa3
 }
9d59029a
 
 static inline bool
 tls_crypt_v2_read_keyfile(struct buffer *key, const char *pem_name,
                           const char *key_file, const char *key_inline)
 {
     bool ret = false;
     struct buffer key_pem = { 0 };
     struct gc_arena gc = gc_new();
 
     if (strcmp(key_file, INLINE_FILE_TAG))
     {
         key_pem = buffer_read_from_file(key_file, &gc);
         if (!buf_valid(&key_pem))
         {
             msg(M_WARN, "ERROR: failed to read tls-crypt-v2 key file (%s)",
                 key_file);
             goto cleanup;
         }
     }
     else
     {
         buf_set_read(&key_pem, (const void *)key_inline, strlen(key_inline));
     }
 
     if (!crypto_pem_decode(pem_name, key, &key_pem))
     {
         msg(M_WARN, "ERROR: tls-crypt-v2 pem decode failed");
         goto cleanup;
     }
 
     ret = true;
 cleanup:
     if (strcmp(key_file, INLINE_FILE_TAG))
     {
         buf_clear(&key_pem);
     }
     gc_free(&gc);
     return ret;
 }
 
 static inline void
 tls_crypt_v2_load_client_key(struct key_ctx_bi *key, const struct key2 *key2,
                              bool tls_server)
 {
     const int key_direction = tls_server ?
                               KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE;
     struct key_type kt = tls_crypt_kt();
     if (!kt.cipher || !kt.digest)
     {
         msg(M_FATAL, "ERROR: --tls-crypt-v2 not supported");
     }
     init_key_ctx_bi(key, key2, key_direction, &kt,
                     "Control Channel Encryption");
 }
 
 void
 tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf,
                              const char *key_file, const char *key_inline)
 {
     struct buffer client_key = alloc_buf(TLS_CRYPT_V2_CLIENT_KEY_LEN
                                          + TLS_CRYPT_V2_MAX_WKC_LEN);
 
     if (!tls_crypt_v2_read_keyfile(&client_key, tls_crypt_v2_cli_pem_name,
                                    key_file, key_inline))
     {
         msg(M_FATAL, "ERROR: invalid tls-crypt-v2 client key format");
     }
 
     struct key2 key2;
     if (!buf_read(&client_key, &key2.keys, sizeof(key2.keys)))
     {
         msg(M_FATAL, "ERROR: not enough data in tls-crypt-v2 client key");
     }
 
     tls_crypt_v2_load_client_key(key, &key2, false);
     secure_memzero(&key2, sizeof(key2));
 
     *wkc_buf = client_key;
 }
 
 void
 tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt,
                              const char *key_file, const char *key_inline)
 {
     struct key srv_key;
     struct buffer srv_key_buf;
 
     buf_set_write(&srv_key_buf, (void *)&srv_key, sizeof(srv_key));
     if (!tls_crypt_v2_read_keyfile(&srv_key_buf, tls_crypt_v2_srv_pem_name,
                                    key_file, key_inline))
     {
         msg(M_FATAL, "ERROR: invalid tls-crypt-v2 server key format");
     }
 
     struct key_type kt = tls_crypt_kt();
     if (!kt.cipher || !kt.digest)
     {
         msg(M_FATAL, "ERROR: --tls-crypt-v2 not supported");
     }
     init_key_ctx(key_ctx, &srv_key, &kt, encrypt, "tls-crypt-v2 server key");
     secure_memzero(&srv_key, sizeof(srv_key));
 }
 
 static bool
 tls_crypt_v2_wrap_client_key(struct buffer *wkc,
                              const struct key2 *src_key,
                              const struct buffer *src_metadata,
                              struct key_ctx *server_key, struct gc_arena *gc)
 {
     cipher_ctx_t *cipher_ctx = server_key->cipher;
     struct buffer work = alloc_buf_gc(TLS_CRYPT_V2_MAX_WKC_LEN
                                       + cipher_ctx_block_size(cipher_ctx), gc);
 
     /* Calculate auth tag and synthetic IV */
     uint8_t *tag = buf_write_alloc(&work, TLS_CRYPT_TAG_SIZE);
     if (!tag)
     {
         msg(M_WARN, "ERROR: could not write tag");
         return false;
     }
     uint16_t net_len = htons(sizeof(src_key->keys) + BLEN(src_metadata)
                              + TLS_CRYPT_V2_TAG_SIZE + sizeof(uint16_t));
     hmac_ctx_t *hmac_ctx = server_key->hmac;
     hmac_ctx_reset(hmac_ctx);
     hmac_ctx_update(hmac_ctx, (void *)&net_len, sizeof(net_len));
     hmac_ctx_update(hmac_ctx, (void *)src_key->keys, sizeof(src_key->keys));
     hmac_ctx_update(hmac_ctx, BPTR(src_metadata), BLEN(src_metadata));
     hmac_ctx_final(hmac_ctx, tag);
 
     dmsg(D_CRYPTO_DEBUG, "TLS-CRYPT WRAP TAG: %s",
          format_hex(tag, TLS_CRYPT_TAG_SIZE, 0, gc));
 
     /* Use the 128 most significant bits of the tag as IV */
     ASSERT(cipher_ctx_reset(cipher_ctx, tag));
 
     /* Overflow check (OpenSSL requires an extra block in the dst buffer) */
     if (buf_forward_capacity(&work) < (sizeof(src_key->keys)
                                        + BLEN(src_metadata)
                                        + sizeof(net_len)
                                        + cipher_ctx_block_size(cipher_ctx)))
     {
         msg(M_WARN, "ERROR: could not crypt: insufficient space in dst");
         return false;
     }
 
     /* Encrypt */
     int outlen = 0;
     ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen,
                              (void *)src_key->keys, sizeof(src_key->keys)));
     ASSERT(buf_inc_len(&work, outlen));
     ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen,
                              BPTR(src_metadata), BLEN(src_metadata)));
     ASSERT(buf_inc_len(&work, outlen));
     ASSERT(cipher_ctx_final(cipher_ctx, BEND(&work), &outlen));
     ASSERT(buf_inc_len(&work, outlen));
     ASSERT(buf_write(&work, &net_len, sizeof(net_len)));
 
     return buf_copy(wkc, &work);
 }
 
a98a5676
 static bool
 tls_crypt_v2_unwrap_client_key(struct key2 *client_key, struct buffer *metadata,
                                struct buffer wrapped_client_key,
                                struct key_ctx *server_key)
 {
     const char *error_prefix = __func__;
     bool ret = false;
     struct gc_arena gc = gc_new();
     /* The crypto API requires one extra cipher block of buffer head room when
      * decrypting, which nicely matches the tag size of WKc.  So
      * TLS_CRYPT_V2_MAX_WKC_LEN is always large enough for the plaintext. */
     uint8_t plaintext_buf_data[TLS_CRYPT_V2_MAX_WKC_LEN] = { 0 };
     struct buffer plaintext = { 0 };
 
     dmsg(D_TLS_DEBUG_MED, "%s: unwrapping client key (len=%d): %s", __func__,
          BLEN(&wrapped_client_key), format_hex(BPTR(&wrapped_client_key),
                                                BLEN(&wrapped_client_key),
                                                0, &gc));
 
     if (TLS_CRYPT_V2_MAX_WKC_LEN < BLEN(&wrapped_client_key))
     {
         CRYPT_ERROR("wrapped client key too big");
     }
 
     /* Decrypt client key and metadata */
     uint16_t net_len = 0;
     const uint8_t *tag = BPTR(&wrapped_client_key);
 
     if (BLEN(&wrapped_client_key) < sizeof(net_len))
     {
         CRYPT_ERROR("failed to read length");
     }
     memcpy(&net_len, BEND(&wrapped_client_key) - sizeof(net_len),
            sizeof(net_len));
 
     if (ntohs(net_len) != BLEN(&wrapped_client_key))
     {
         dmsg(D_TLS_DEBUG_LOW, "%s: net_len=%u, BLEN=%i", __func__,
              ntohs(net_len), BLEN(&wrapped_client_key));
         CRYPT_ERROR("invalid length");
     }
 
     buf_inc_len(&wrapped_client_key, -(int)sizeof(net_len));
 
     if (!buf_advance(&wrapped_client_key, TLS_CRYPT_TAG_SIZE))
     {
         CRYPT_ERROR("failed to read tag");
     }
 
     if (!cipher_ctx_reset(server_key->cipher, tag))
     {
         CRYPT_ERROR("failed to initialize IV");
     }
     buf_set_write(&plaintext, plaintext_buf_data, sizeof(plaintext_buf_data));
     int outlen = 0;
     if (!cipher_ctx_update(server_key->cipher, BPTR(&plaintext), &outlen,
                            BPTR(&wrapped_client_key),
                            BLEN(&wrapped_client_key)))
     {
         CRYPT_ERROR("could not decrypt client key");
     }
     ASSERT(buf_inc_len(&plaintext, outlen));
 
     if (!cipher_ctx_final(server_key->cipher, BEND(&plaintext), &outlen))
     {
         CRYPT_ERROR("cipher final failed");
     }
     ASSERT(buf_inc_len(&plaintext, outlen));
 
     /* Check authentication */
     uint8_t tag_check[TLS_CRYPT_TAG_SIZE] = { 0 };
     hmac_ctx_reset(server_key->hmac);
     hmac_ctx_update(server_key->hmac, (void *)&net_len, sizeof(net_len));
     hmac_ctx_update(server_key->hmac, BPTR(&plaintext),
                     BLEN(&plaintext));
     hmac_ctx_final(server_key->hmac, tag_check);
 
     if (memcmp_constant_time(tag, tag_check, sizeof(tag_check)))
     {
         dmsg(D_CRYPTO_DEBUG, "tag      : %s",
              format_hex(tag, sizeof(tag_check), 0, &gc));
         dmsg(D_CRYPTO_DEBUG, "tag_check: %s",
              format_hex(tag_check, sizeof(tag_check), 0, &gc));
         CRYPT_ERROR("client key authentication error");
     }
 
     if (buf_len(&plaintext) < sizeof(client_key->keys))
     {
         CRYPT_ERROR("failed to read client key");
     }
     memcpy(&client_key->keys, BPTR(&plaintext), sizeof(client_key->keys));
     ASSERT(buf_advance(&plaintext, sizeof(client_key->keys)));
 
     if(!buf_copy(metadata, &plaintext))
     {
         CRYPT_ERROR("metadata too large for supplied buffer");
     }
 
     ret = true;
 error_exit:
     if (!ret)
     {
         secure_memzero(client_key, sizeof(*client_key));
     }
     buf_clear(&plaintext);
     gc_free(&gc);
     return ret;
 }
 
19dffdbd
 bool
 tls_crypt_v2_extract_client_key(struct buffer *buf,
                                 struct tls_wrap_ctx *ctx)
 {
     if (!ctx->tls_crypt_v2_server_key.cipher)
     {
         msg(D_TLS_ERRORS,
              "Client wants tls-crypt-v2, but no server key present.");
         return false;
     }
 
     msg(D_HANDSHAKE, "Control Channel: using tls-crypt-v2 key");
 
     struct buffer wrapped_client_key = *buf;
     uint16_t net_len = 0;
 
     if (BLEN(&wrapped_client_key) < sizeof(net_len))
     {
         msg(D_TLS_ERRORS, "failed to read length");
     }
     memcpy(&net_len, BEND(&wrapped_client_key) - sizeof(net_len),
            sizeof(net_len));
 
     size_t wkc_len = ntohs(net_len);
     if (!buf_advance(&wrapped_client_key, BLEN(&wrapped_client_key) - wkc_len))
     {
         msg(D_TLS_ERRORS, "Can not locate tls-crypt-v2 client key");
         return false;
     }
 
     struct key2 client_key = { 0 };
     ctx->tls_crypt_v2_metadata = alloc_buf(TLS_CRYPT_V2_MAX_METADATA_LEN);
     if (!tls_crypt_v2_unwrap_client_key(&client_key,
                                         &ctx->tls_crypt_v2_metadata,
                                         wrapped_client_key,
                                         &ctx->tls_crypt_v2_server_key))
     {
         msg(D_TLS_ERRORS, "Can not unwrap tls-crypt-v2 client key");
         secure_memzero(&client_key, sizeof(client_key));
         return false;
     }
 
     /* Load the decrypted key */
     ctx->mode = TLS_WRAP_CRYPT;
     ctx->cleanup_key_ctx = true;
     ctx->opt.flags |= CO_PACKET_ID_LONG_FORM;
     memset(&ctx->opt.key_ctx_bi, 0, sizeof(ctx->opt.key_ctx_bi));
     tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi, &client_key, true);
     secure_memzero(&client_key, sizeof(client_key));
 
     /* Remove client key from buffer so tls-crypt code can unwrap message */
     ASSERT(buf_inc_len(buf, -(BLEN(&wrapped_client_key))));
 
     return true;
 }
 
9d59029a
 void
 tls_crypt_v2_write_server_key_file(const char *filename)
 {
     struct gc_arena gc = gc_new();
     struct key server_key = { 0 };
     struct buffer server_key_buf = clear_buf();
     struct buffer server_key_pem = clear_buf();
 
     if (!rand_bytes((void *)&server_key, sizeof(server_key)))
     {
         msg(M_NONFATAL, "ERROR: could not generate random key");
         goto cleanup;
     }
     buf_set_read(&server_key_buf, (void *)&server_key, sizeof(server_key));
     if (!crypto_pem_encode(tls_crypt_v2_srv_pem_name, &server_key_pem,
                            &server_key_buf, &gc))
     {
         msg(M_WARN, "ERROR: could not PEM-encode server key");
         goto cleanup;
     }
 
     if (!buffer_write_file(filename, &server_key_pem))
     {
         msg(M_ERR, "ERROR: could not write server key file");
         goto cleanup;
     }
 
 cleanup:
     secure_memzero(&server_key, sizeof(server_key));
     buf_clear(&server_key_pem);
     gc_free(&gc);
     return;
 }
 
 void
 tls_crypt_v2_write_client_key_file(const char *filename,
                                    const char *b64_metadata,
                                    const char *server_key_file,
                                    const char *server_key_inline)
 {
     struct gc_arena gc = gc_new();
     struct key_ctx server_key = { 0 };
     struct buffer client_key_pem = { 0 };
     struct buffer dst = alloc_buf_gc(TLS_CRYPT_V2_CLIENT_KEY_LEN
                                      + TLS_CRYPT_V2_MAX_WKC_LEN, &gc);
     struct key2 client_key = { 2 };
 
     if (!rand_bytes((void *)client_key.keys, sizeof(client_key.keys)))
     {
         msg(M_FATAL, "ERROR: could not generate random key");
         goto cleanup;
     }
     ASSERT(buf_write(&dst, client_key.keys, sizeof(client_key.keys)));
 
     struct buffer metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN, &gc);
     if (b64_metadata)
     {
         if (TLS_CRYPT_V2_MAX_B64_METADATA_LEN < strlen(b64_metadata))
         {
             msg(M_FATAL,
                 "ERROR: metadata too long (%d bytes, max %u bytes)",
                 (int)strlen(b64_metadata), TLS_CRYPT_V2_MAX_B64_METADATA_LEN);
         }
         ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_USER, 1));
         int decoded_len = openvpn_base64_decode(b64_metadata, BPTR(&metadata),
                                                 BCAP(&metadata));
         if (decoded_len < 0)
         {
             msg(M_FATAL, "ERROR: failed to base64 decode provided metadata");
             goto cleanup;
         }
         ASSERT(buf_inc_len(&metadata, decoded_len));
     }
     else
     {
         int64_t timestamp = htonll(now);
         ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_TIMESTAMP, 1));
         ASSERT(buf_write(&metadata, &timestamp, sizeof(timestamp)));
     }
 
     tls_crypt_v2_init_server_key(&server_key, true, server_key_file,
                                  server_key_inline);
     if (!tls_crypt_v2_wrap_client_key(&dst, &client_key, &metadata, &server_key,
                                       &gc))
     {
         msg(M_FATAL, "ERROR: could not wrap generated client key");
         goto cleanup;
     }
 
     /* PEM-encode Kc || WKc */
     if (!crypto_pem_encode(tls_crypt_v2_cli_pem_name, &client_key_pem, &dst,
                            &gc))
     {
         msg(M_FATAL, "ERROR: could not PEM-encode client key");
         goto cleanup;
     }
 
     if (!buffer_write_file(filename, &client_key_pem))
     {
         msg(M_FATAL, "ERROR: could not write client key file");
         goto cleanup;
     }
 
     /* Sanity check: load client key (as "client") */
     struct key_ctx_bi test_client_key;
     struct buffer test_wrapped_client_key;
     msg(D_GENKEY, "Testing client-side key loading...");
     tls_crypt_v2_init_client_key(&test_client_key, &test_wrapped_client_key,
                                  filename, NULL);
     free_key_ctx_bi(&test_client_key);
a98a5676
 
     /* Sanity check: unwrap and load client key (as "server") */
     struct buffer test_metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN,
                                                &gc);
     struct key2 test_client_key2 = { 0 };
     free_key_ctx(&server_key);
     tls_crypt_v2_init_server_key(&server_key, false, server_key_file,
                                  server_key_inline);
     msg(D_GENKEY, "Testing server-side key loading...");
     ASSERT(tls_crypt_v2_unwrap_client_key(&test_client_key2, &test_metadata,
                                           test_wrapped_client_key, &server_key));
     secure_memzero(&test_client_key2, sizeof(test_client_key2));
9d59029a
     free_buf(&test_wrapped_client_key);
 
 cleanup:
     secure_memzero(&client_key, sizeof(client_key));
     free_key_ctx(&server_key);
     buf_clear(&client_key_pem);
     buf_clear(&dst);
 
     gc_free(&gc);
 }