Commit message: Use constant time memcmp when comparing HMACs in openvpn_decrypt.

Signed-off-by: Steffan Karger <steffan.karger@fox-it.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Signed-off-by: Gert Doering <gert@greenie.muc.de>
CVE: CVE-2013-2061
CWE: CWE-200
Vulnerability description: The openvpn_decrypt function in crypto.c in OpenVPN 2.3.0 and earlier, when running in UDP mode, allows remote attackers to obtain sensitive information via a timing attack involving an HMAC comparison function that does not run in constant time and a padding oracle attack on the CBC mode cipher.

Project: openvpn
File: crypto.c

cfb_ofb_mode (const struct key_type* kt)
{
  if (kt && kt->cipher) {
      const unsigned int mode = cipher_kt_mode (kt->cipher);
      return mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB;
  }
  return false;
}

Project: openvpn
File: crypto.c

check_key (struct key *key, const struct key_type *kt)
{
  if (kt->cipher)
    {
      /*
       * Check for zero key
       */
      if (key_is_zero(key, kt))
	return false;

      /*
       * Check for weak or semi-weak DES keys.
       */
      {
	const int ndc = key_des_num_cblocks (kt->cipher);
	if (ndc)
	  return key_des_check (key->cipher, kt->cipher_length, ndc);
	else
	  return true;
      }
    }
  return true;
}

Project: openvpn
File: crypto.c

check_replay_iv_consistency (const struct key_type *kt, bool packet_id, bool use_iv)
{
  if (cfb_ofb_mode (kt) && !(packet_id && use_iv))
    msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB mode cipher");
}

Project: openvpn
File: crypto.c

crypto_adjust_frame_parameters(struct frame *frame,
			       const struct key_type* kt,
			       bool cipher_defined,
			       bool use_iv,
			       bool packet_id,
			       bool packet_id_long_form)
{
  frame_add_to_extra_frame (frame,
			    (packet_id ? packet_id_size (packet_id_long_form) : 0) +
			    ((cipher_defined && use_iv) ? cipher_kt_iv_size (kt->cipher) : 0) +
			    (cipher_defined ? cipher_kt_block_size (kt->cipher) : 0) + /* worst case padding expansion */
			    kt->hmac_length);
}

Project: openvpn
File: crypto.c

fixup_key (struct key *key, const struct key_type *kt)
{
  struct gc_arena gc = gc_new ();
  if (kt->cipher)
    {
#ifdef ENABLE_DEBUG
      const struct key orig = *key;
#endif
      const int ndc = key_des_num_cblocks (kt->cipher);

      if (ndc)
	key_des_fixup (key->cipher, kt->cipher_length, ndc);

#ifdef ENABLE_DEBUG
      if (check_debug_level (D_CRYPTO_DEBUG))
	{
	  if (memcmp (orig.cipher, key->cipher, kt->cipher_length))
	    dmsg (D_CRYPTO_DEBUG, "CRYPTO INFO: fixup_key: before=%s after=%s",
		 format_hex (orig.cipher, kt->cipher_length, 0, &gc),
		 format_hex (key->cipher, kt->cipher_length, 0, &gc));
	}
#endif
    }
  gc_free (&gc);
}

Project: openvpn
File: crypto.c

free_key_ctx (struct key_ctx *ctx)
{
  if (ctx->cipher)
    {
      cipher_ctx_cleanup(ctx->cipher);
      free(ctx->cipher);
      ctx->cipher = NULL;
    }
  if (ctx->hmac)
    {
      hmac_ctx_cleanup(ctx->hmac);
      free(ctx->hmac);
      ctx->hmac = NULL;
    }
}

Project: openvpn
File: crypto.c

free_key_ctx_bi (struct key_ctx_bi *ctx)
{
  free_key_ctx(&ctx->encrypt);
  free_key_ctx(&ctx->decrypt);
}

Project: openvpn
File: crypto.c

free_ssl_lib (void)
{
  crypto_uninit_lib ();
  prng_uninit();
}

Project: openvpn
File: crypto.c

generate_key_random (struct key *key, const struct key_type *kt)
{
  int cipher_len = MAX_CIPHER_KEY_LENGTH;
  int hmac_len = MAX_HMAC_KEY_LENGTH;

  struct gc_arena gc = gc_new ();

  do {
    CLEAR (*key);
    if (kt)
      {
	if (kt->cipher && kt->cipher_length > 0 && kt->cipher_length <= cipher_len)
	  cipher_len = kt->cipher_length;

	if (kt->digest && kt->hmac_length > 0 && kt->hmac_length <= hmac_len)
	  hmac_len = kt->hmac_length;
      }
    if (!rand_bytes (key->cipher, cipher_len)
	|| !rand_bytes (key->hmac, hmac_len))
      msg (M_FATAL, "ERROR: Random number generator cannot obtain entropy for key generation");

    dmsg (D_SHOW_KEY_SOURCE, "Cipher source entropy: %s", format_hex (key->cipher, cipher_len, 0, &gc));
    dmsg (D_SHOW_KEY_SOURCE, "HMAC source entropy: %s", format_hex (key->hmac, hmac_len, 0, &gc));

    if (kt)
      fixup_key (key, kt);
  } while (kt && !check_key (key, kt));

  gc_free (&gc);
}

Project: openvpn
File: crypto.c

get_random()
{
  long int l;
  prng_bytes ((unsigned char *)&l, sizeof(l));
  if (l < 0)
    l = -l;
  return l;
}

Project: openvpn
File: crypto.c

get_tls_handshake_key (const struct key_type *key_type,
		       struct key_ctx_bi *ctx,
		       const char *passphrase_file,
		       const int key_direction,
		       const unsigned int flags)
{
  if (passphrase_file && key_type->hmac_length)
    {
      struct key2 key2;
      struct key_type kt = *key_type;
      struct key_direction_state kds;

      /* for control channel we are only authenticating, not encrypting */
      kt.cipher_length = 0;
      kt.cipher = NULL;

      if (flags & GHK_INLINE)
	{
	  /* key was specified inline, key text is in passphrase_file */
	  read_key_file (&key2, passphrase_file, RKF_INLINE|RKF_MUST_SUCCEED);

	  /* succeeded? */
	  if (key2.n == 2)
	    msg (M_INFO, "Control Channel Authentication: tls-auth using INLINE static key file");
	  else
	    msg (M_FATAL, "INLINE tls-auth file lacks the requisite 2 keys");
	}
      else
      {
	/* first try to parse as an OpenVPN static key file */
	read_key_file (&key2, passphrase_file, 0);

	/* succeeded? */
	if (key2.n == 2)
	  {
	    msg (M_INFO,
		 "Control Channel Authentication: using '%s' as a " PACKAGE_NAME " static key file",
		 passphrase_file);
	  }
	else
	  {
	    int hash_size;

	    CLEAR (key2);

	    /* failed, now try to get hash from a freeform file */
	    hash_size = read_passphrase_hash (passphrase_file,
					      kt.digest,
					      key2.keys[0].hmac,
					      MAX_HMAC_KEY_LENGTH);
	    ASSERT (hash_size == kt.hmac_length);

	    /* suceeded */
	    key2.n = 1;

	    msg (M_INFO,
		 "Control Channel Authentication: using '%s' as a free-form passphrase file",
		 passphrase_file);
	  }
      }
      /* handle key direction */

      key_direction_state_init (&kds, key_direction);
      must_have_n_keys (passphrase_file, "tls-auth", &key2, kds.need_keys);

      /* initialize hmac key in both directions */

      init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], &kt, OPENVPN_OP_ENCRYPT,
		    "Outgoing Control Channel Authentication");
      init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, OPENVPN_OP_DECRYPT,
		    "Incoming Control Channel Authentication");

      CLEAR (key2);
    }
  else
    {
      CLEAR (*ctx);
    }
}

Project: openvpn
File: crypto.c

init_ssl_lib (void)
{
  crypto_init_lib ();
}

Project: openvpn
File: crypto.c

key2_print (const struct key2* k,
	    const struct key_type *kt,
	    const char* prefix0,
	    const char* prefix1)
{
  struct gc_arena gc = gc_new ();
  ASSERT (k->n == 2);
  dmsg (D_SHOW_KEY_SOURCE, "%s (cipher): %s",
       prefix0,
       format_hex (k->keys[0].cipher, kt->cipher_length, 0, &gc));
  dmsg (D_SHOW_KEY_SOURCE, "%s (hmac): %s",
       prefix0,
       format_hex (k->keys[0].hmac, kt->hmac_length, 0, &gc));
  dmsg (D_SHOW_KEY_SOURCE, "%s (cipher): %s",
       prefix1,
       format_hex (k->keys[1].cipher, kt->cipher_length, 0, &gc));
  dmsg (D_SHOW_KEY_SOURCE, "%s (hmac): %s",
       prefix1,
       format_hex (k->keys[1].hmac, kt->hmac_length, 0, &gc));
  gc_free (&gc);
}

Project: openvpn
File: crypto.c

key_direction_state_init (struct key_direction_state *kds, int key_direction)
{
  CLEAR (*kds);
  switch (key_direction)
    {
    case KEY_DIRECTION_NORMAL:
      kds->out_key = 0;
      kds->in_key = 1;
      kds->need_keys = 2;
      break;
    case KEY_DIRECTION_INVERSE:
      kds->out_key = 1;
      kds->in_key = 0;
      kds->need_keys = 2;
      break;
    case KEY_DIRECTION_BIDIRECTIONAL:
      kds->out_key = 0;
      kds->in_key = 0;
      kds->need_keys = 1;
      break;
    default:
      ASSERT (0);
    }
}

Project: openvpn
File: crypto.c

key_is_zero (struct key *key, const struct key_type *kt)
{
  int i;
  for (i = 0; i < kt->cipher_length; ++i)
    if (key->cipher[i])
      return false;
  msg (D_CRYPT_ERRORS, "CRYPTO INFO: WARNING: zero key detected");
  return true;
}

Project: openvpn
File: crypto.c

keydirection2ascii (int kd, bool remote)
{
  if (kd == KEY_DIRECTION_BIDIRECTIONAL)
    return NULL;
  else if (kd == KEY_DIRECTION_NORMAL)
    return remote ? "1" : "0";
  else if (kd == KEY_DIRECTION_INVERSE)
    return remote ? "0" : "1";
  else
    {
      ASSERT (0);
    }
  return NULL; /* NOTREACHED */
}

Project: openvpn
File: crypto.c

md5_digest_clear (struct md5_digest *digest)
{
  CLEAR (*digest);
}

Project: openvpn
File: crypto.c

md5_digest_defined (const struct md5_digest *digest)
{
  int i;
  for (i = 0; i < MD5_DIGEST_LENGTH; ++i)
    if (digest->digest[i])
      return true;
  return false;
}

Project: openvpn
File: crypto.c

md5_digest_equal (const struct md5_digest *d1, const struct md5_digest *d2)
{
  return memcmp(d1->digest, d2->digest, MD5_DIGEST_LENGTH) == 0;
}

Project: openvpn
File: crypto.c

md5_state_final (struct md5_state *s, struct md5_digest *out)
{
  md_ctx_final(&s->ctx, out->digest);
  md_ctx_cleanup(&s->ctx);
}

Project: openvpn
File: crypto.c

md5_state_init (struct md5_state *s)
{
  const md_kt_t *md5_kt = md_kt_get("MD5");

  md_ctx_init(&s->ctx, md5_kt);
}

Project: openvpn
File: crypto.c

must_have_n_keys (const char *filename, const char *option, const struct key2 *key2, int n)
{
  if (key2->n < n)
    {
#ifdef ENABLE_SMALL
      msg (M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d]", filename, option, key2->n, n);
#else
      msg (M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with '" PACKAGE " --genkey --secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter", filename, option, key2->n, n, option);
#endif
    }
}

Project: openvpn
File: crypto.c

 openvpn_encrypt (struct buffer *buf, struct buffer work,
 		 const struct crypto_options *opt,
		 const struct frame* frame)
{
  struct gc_arena gc;
  gc_init (&gc);

  if (buf->len > 0 && opt->key_ctx_bi)
    {
      struct key_ctx *ctx = &opt->key_ctx_bi->encrypt;

      /* Do Encrypt from buf -> work */
      if (ctx->cipher)
	{
	  uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH];
	  const int iv_size = cipher_ctx_iv_length (ctx->cipher);
	  const unsigned int mode = cipher_ctx_mode (ctx->cipher);
	  int outlen;

	  if (mode == OPENVPN_MODE_CBC)
	    {
	      CLEAR (iv_buf);

	      /* generate pseudo-random IV */
	      if (opt->flags & CO_USE_IV)
		prng_bytes (iv_buf, iv_size);

	      /* Put packet ID in plaintext buffer or IV, depending on cipher mode */
	      if (opt->packet_id)
		{
		  struct packet_id_net pin;
		  packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
		  ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
		}
	    }
	  else if (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB)
	    {
	      struct packet_id_net pin;
	      struct buffer b;

	      ASSERT (opt->flags & CO_USE_IV);    /* IV and packet-ID required */
	      ASSERT (opt->packet_id); /*  for this mode. */

	      packet_id_alloc_outgoing (&opt->packet_id->send, &pin, true);
	      memset (iv_buf, 0, iv_size);
	      buf_set_write (&b, iv_buf, iv_size);
	      ASSERT (packet_id_write (&pin, &b, true, false));
	    }
	  else /* We only support CBC, CFB, or OFB modes right now */
	    {
	      ASSERT (0);
	    }

	  /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
	  ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));

	  /* set the IV pseudo-randomly */
	  if (opt->flags & CO_USE_IV)
	    dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc));

	  dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s",
	       format_hex (BPTR (buf), BLEN (buf), 80, &gc));

	  /* cipher_ctx was already initialized with key & keylen */
	  ASSERT (cipher_ctx_reset(ctx->cipher, iv_buf));

	  /* Buffer overflow check */
	  if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher)))
	    {
	      msg (D_CRYPT_ERRORS, "ENCRYPT: buffer size error, bc=%d bo=%d bl=%d wc=%d wo=%d wl=%d cbs=%d",
		   buf->capacity,
		   buf->offset,
		   buf->len,
		   work.capacity,
		   work.offset,
		   work.len,
		   cipher_ctx_block_size (ctx->cipher));
	      goto err;
	    }

	  /* Encrypt packet ID, payload */
	  ASSERT (cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf)));
	  work.len += outlen;

	  /* Flush the encryption buffer */
	  ASSERT(cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen));
	  work.len += outlen;
	  ASSERT (outlen == iv_size);

	  /* prepend the IV to the ciphertext */
	  if (opt->flags & CO_USE_IV)
	    {
	      uint8_t *output = buf_prepend (&work, iv_size);
	      ASSERT (output);
	      memcpy (output, iv_buf, iv_size);
	    }

	  dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s",
	       format_hex (BPTR (&work), BLEN (&work), 80, &gc));
	}
      else				/* No Encryption */
	{
	  if (opt->packet_id)
	    {
	      struct packet_id_net pin;
	      packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
	      ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
	    }
	  work = *buf;
	}

      /* HMAC the ciphertext (or plaintext if !cipher) */
      if (ctx->hmac)
	{
	  uint8_t *output = NULL;

	  hmac_ctx_reset (ctx->hmac);
	  hmac_ctx_update (ctx->hmac, BPTR(&work), BLEN(&work));
	  output = buf_prepend (&work, hmac_ctx_size(ctx->hmac));
	  ASSERT (output);
	  hmac_ctx_final (ctx->hmac, output);
	}

      *buf = work;
    }

  gc_free (&gc);
  return;

err:
  crypto_clear_error();
  buf->len = 0;
  gc_free (&gc);
  return;
}

Project: openvpn
File: crypto.c

prng_bytes (uint8_t *output, int len)
{
  static size_t processed = 0;

  if (nonce_md)
    {
      const int md_size = md_kt_size (nonce_md);
      while (len > 0)
	{
	  unsigned int outlen = 0;
	  const int blen = min_int (len, md_size);
	  md_full(nonce_md, nonce_data, md_size + nonce_secret_len, nonce_data);
	  memcpy (output, nonce_data, blen);
	  output += blen;
	  len -= blen;

	  /* Ensure that random data is reset regularly */
	  processed += blen;
	  if(processed > PRNG_NONCE_RESET_BYTES) {
	    prng_reset_nonce();
	    processed = 0;
	  }
	}
    }
  else
    rand_bytes (output, len);
}

Project: openvpn
File: crypto.c

prng_init (const char *md_name, const int nonce_secret_len_parm)
{
  prng_uninit ();
  nonce_md = md_name ? md_kt_get (md_name) : NULL;
  if (nonce_md)
    {
      ASSERT (nonce_secret_len_parm >= NONCE_SECRET_LEN_MIN && nonce_secret_len_parm <= NONCE_SECRET_LEN_MAX);
      nonce_secret_len = nonce_secret_len_parm;
      {
	const int size = md_kt_size(nonce_md) + nonce_secret_len;
	dmsg (D_CRYPTO_DEBUG, "PRNG init md=%s size=%d", md_kt_name(nonce_md), size);
	nonce_data = (uint8_t*) malloc (size);
	check_malloc_return (nonce_data);
	prng_reset_nonce();
      }
    }
}

Project: openvpn
File: crypto.c

prng_reset_nonce ()
{
  const int size = md_kt_size (nonce_md) + nonce_secret_len;
#if 1 /* Must be 1 for real usage */
  if (!rand_bytes (nonce_data, size))
    msg (M_FATAL, "ERROR: Random number generator cannot obtain entropy for PRNG");
#else
    /* Only for testing -- will cause a predictable PRNG sequence */
    {
      int i;
      for (i = 0; i < size; ++i)
	nonce_data[i] = (uint8_t) i;
    }
#endif
}

Project: openvpn
File: crypto.c

prng_uninit (void)
{
  free (nonce_data);
  nonce_data = NULL;
  nonce_md = NULL;
  nonce_secret_len = 0;
}

Project: openvpn
File: crypto.c

read_key_file (struct key2 *key2, const char *file, const unsigned int flags)
{
  struct gc_arena gc = gc_new ();
  struct buffer in;
  int fd, size;
  uint8_t hex_byte[3] = {0, 0, 0};
  const char *error_filename = file;

  /* parse info */
  const unsigned char *cp;
  int hb_index = 0;
  int line_num = 1;
  int line_index = 0;
  int match = 0;

  /* output */
  uint8_t* out = (uint8_t*) &key2->keys;
  const int keylen = sizeof (key2->keys);
  int count = 0;

  /* parse states */
# define PARSE_INITIAL        0
# define PARSE_HEAD           1
# define PARSE_DATA           2
# define PARSE_DATA_COMPLETE  3
# define PARSE_FOOT           4
# define PARSE_FINISHED       5
  int state = PARSE_INITIAL;

  /* constants */
  const int hlen = strlen (static_key_head);
  const int flen = strlen (static_key_foot);
  const int onekeylen = sizeof (key2->keys[0]);

  CLEAR (*key2);

  /*
   * Key can be provided as a filename in 'file' or if RKF_INLINE
   * is set, the actual key data itself in ascii form.
   */
  if (flags & RKF_INLINE) /* 'file' is a string containing ascii representation of key */
    {
      size = strlen (file) + 1;
      buf_set_read (&in, (const uint8_t *)file, size);
      error_filename = INLINE_FILE_TAG;
    }
  else /* 'file' is a filename which refers to a file containing the ascii key */
    {
      in = alloc_buf_gc (2048, &gc);
      fd = platform_open (file, O_RDONLY, 0);
      if (fd == -1)
	msg (M_ERR, "Cannot open file key file '%s'", file);
      size = read (fd, in.data, in.capacity);
      if (size < 0)
	msg (M_FATAL, "Read error on key file ('%s')", file);
      if (size == in.capacity)
	msg (M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity);
      close (fd);
    }

  cp = (unsigned char *)in.data;
  while (size > 0)
    {
      const unsigned char c = *cp;

#if 0
      msg (M_INFO, "char='%c'[%d] s=%d ln=%d li=%d m=%d c=%d",
	   c, (int)c, state, line_num, line_index, match, count);
#endif

      if (c == '\n')
	{
	  line_index = match = 0;
	  ++line_num;	      
	}
      else
	{
	  /* first char of new line */
	  if (!line_index)
	    {
	      /* first char of line after header line? */
	      if (state == PARSE_HEAD)
		state = PARSE_DATA;

	      /* first char of footer */
	      if ((state == PARSE_DATA || state == PARSE_DATA_COMPLETE) && c == '-')
		state = PARSE_FOOT;
	    }

	  /* compare read chars with header line */
	  if (state == PARSE_INITIAL)
	    {
	      if (line_index < hlen && c == static_key_head[line_index])
		{
		  if (++match == hlen)
		    state = PARSE_HEAD;
		}
	    }

	  /* compare read chars with footer line */
	  if (state == PARSE_FOOT)
	    {
	      if (line_index < flen && c == static_key_foot[line_index])
		{
		  if (++match == flen)
		    state = PARSE_FINISHED;
		}
	    }

	  /* reading key */
	  if (state == PARSE_DATA)
	    {
	      if (isxdigit(c))
		{
		  ASSERT (hb_index >= 0 && hb_index < 2);
		  hex_byte[hb_index++] = c;
		  if (hb_index == 2)
		    {
		      unsigned int u;
		      ASSERT(sscanf((const char *)hex_byte, "%x", &u) == 1);
		      *out++ = u;
		      hb_index = 0;
		      if (++count == keylen)
			state = PARSE_DATA_COMPLETE;
		    }
		}
	      else if (isspace(c))
		;
	      else
		{
		  msg (M_FATAL,
		       (isprint (c) ? printable_char_fmt : unprintable_char_fmt),
		       c, line_num, error_filename, count, onekeylen, keylen);
		}
	    }
	  ++line_index;
	}
      ++cp;
      --size;
    }

  /*
   * Normally we will read either 1 or 2 keys from file.
   */
  key2->n = count / onekeylen;

  ASSERT (key2->n >= 0 && key2->n <= (int) SIZE (key2->keys));

  if (flags & RKF_MUST_SUCCEED)
    {
      if (!key2->n)
	msg (M_FATAL, "Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max)",
	     error_filename, count, onekeylen, keylen);

      if (state != PARSE_FINISHED)
	msg (M_FATAL, "Footer text not found in file '%s' (%d/%d/%d bytes found/min/max)",
	     error_filename, count, onekeylen, keylen);
    }

  /* zero file read buffer if not an inline file */
  if (!(flags & RKF_INLINE))
    buf_clear (&in);

  if (key2->n)
    warn_if_group_others_accessible (error_filename);

#if 0
  /* DEBUGGING */
  {
    int i;
    printf ("KEY READ, n=%d\n", key2->n);
    for (i = 0; i < (int) SIZE (key2->keys); ++i)
      {
	/* format key as ascii */
	const char *fmt = format_hex_ex ((const uint8_t*)&key2->keys[i],
					 sizeof (key2->keys[i]),
					 0,
					 16,
					 "\n",
					 &gc);
	printf ("[%d]\n%s\n\n", i, fmt);
      }
  }
#endif

  /* pop our garbage collection level */
  gc_free (&gc);
}

Project: openvpn
File: crypto.c

read_passphrase_hash (const char *passphrase_file,
		      const md_kt_t *digest,
		      uint8_t *output,
		      int len)
{
  unsigned int outlen = 0;
  md_ctx_t md;

  ASSERT (len >= md_kt_size(digest));
  memset (output, 0, len);

  md_ctx_init(&md, digest);

  /* read passphrase file */
  {
    const int min_passphrase_size = 8;
    uint8_t buf[64];
    int total_size = 0;
    int fd = platform_open (passphrase_file, O_RDONLY, 0);

    if (fd == -1)
      msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file);

    for (;;)
      {
	int size = read (fd, buf, sizeof (buf));
	if (size == 0)
	  break;
	if (size == -1)
	  msg (M_ERR, "Read error on passphrase file: '%s'",
	       passphrase_file);
	md_ctx_update(&md, buf, size);
	total_size += size;
      }
    close (fd);

    warn_if_group_others_accessible (passphrase_file);

    if (total_size < min_passphrase_size)
      msg (M_FATAL,
	   "Passphrase file '%s' is too small (must have at least %d characters)",
	   passphrase_file, min_passphrase_size);
  }
  md_ctx_final(&md, output);
  md_ctx_cleanup(&md);
  return md_kt_size(digest);
}

Project: openvpn
File: crypto.c

test_crypto (const struct crypto_options *co, struct frame* frame)
{
  int i, j;
  struct gc_arena gc = gc_new ();
  struct buffer src = alloc_buf_gc (TUN_MTU_SIZE (frame), &gc);
  struct buffer work = alloc_buf_gc (BUF_SIZE (frame), &gc);
  struct buffer encrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc);
  struct buffer decrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc);
  struct buffer buf = clear_buf();

  /* init work */
  ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));

  msg (M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode.");
  for (i = 1; i <= TUN_MTU_SIZE (frame); ++i)
    {
      update_time ();

      msg (M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i);

      /*
       * Load src with random data.
       */
      ASSERT (buf_init (&src, 0));
      ASSERT (i <= src.capacity);
      src.len = i;
      ASSERT (rand_bytes (BPTR (&src), BLEN (&src)));

      /* copy source to input buf */
      buf = work;
      memcpy (buf_write_alloc (&buf, BLEN (&src)), BPTR (&src), BLEN (&src));

      /* encrypt */
      openvpn_encrypt (&buf, encrypt_workspace, co, frame);

      /* decrypt */
      openvpn_decrypt (&buf, decrypt_workspace, co, frame);

      /* compare */
      if (buf.len != src.len)
	msg (M_FATAL, "SELF TEST FAILED, src.len=%d buf.len=%d", src.len, buf.len);
      for (j = 0; j < i; ++j)
	{
	  const uint8_t in = *(BPTR (&src) + j);
	  const uint8_t out = *(BPTR (&buf) + j);
	  if (in != out)
	    msg (M_FATAL, "SELF TEST FAILED, pos=%d in=%d out=%d", j, in, out);
	}
    }
  msg (M_INFO, PACKAGE_NAME " crypto self-test mode SUCCEEDED.");
  gc_free (&gc);
}

Project: openvpn
File: crypto.c

verify_fix_key2 (struct key2 *key2, const struct key_type *kt, const char *shared_secret_file)
{
  int i;

  for (i = 0; i < key2->n; ++i)
    {
      /* Fix parity for DES keys and make sure not a weak key */
      fixup_key (&key2->keys[i], kt);

      /* This should be a very improbable failure */
      if (!check_key (&key2->keys[i], kt))
	msg (M_FATAL, "Key #%d in '%s' is bad.  Try making a new key with --genkey.",
	     i+1, shared_secret_file);
    }
}

Project: openvpn
File: crypto.c

write_key (const struct key *key, const struct key_type *kt,
	   struct buffer *buf)
{
  ASSERT (kt->cipher_length <= MAX_CIPHER_KEY_LENGTH
	  && kt->hmac_length <= MAX_HMAC_KEY_LENGTH);

  if (!buf_write (buf, &kt->cipher_length, 1))
    return false;
  if (!buf_write (buf, &kt->hmac_length, 1))
    return false;
  if (!buf_write (buf, key->cipher, kt->cipher_length))
    return false;
  if (!buf_write (buf, key->hmac, kt->hmac_length))
    return false;

  return true;
}

Project: openvpn
File: crypto.c

write_key_file (const int nkeys, const char *filename)
{
  struct gc_arena gc = gc_new ();

  int fd, i;
  int nbits = 0;

  /* must be large enough to hold full key file */
  struct buffer out = alloc_buf_gc (2048, &gc);
  struct buffer nbits_head_text = alloc_buf_gc (128, &gc);

  /* how to format the ascii file representation of key */
  const int bytes_per_line = 16;

  /* open key file */
  fd = platform_open (filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);

  if (fd == -1)
    msg (M_ERR, "Cannot open shared secret file '%s' for write", filename);

  buf_printf (&out, "%s\n", static_key_head);

  for (i = 0; i < nkeys; ++i)
    {
      struct key key;
      char* fmt;

      /* generate random bits */
      generate_key_random (&key, NULL);

      /* format key as ascii */
      fmt = format_hex_ex ((const uint8_t*)&key,
			   sizeof (key),
			   0,
			   bytes_per_line,
			   "\n",
			   &gc);

      /* increment random bits counter */
      nbits += sizeof (key) * 8;

      /* write to holding buffer */
      buf_printf (&out, "%s\n", fmt);

      /* zero memory which held key component (will be freed by GC) */
      memset (fmt, 0, strlen(fmt));
      CLEAR (key);
    }

  buf_printf (&out, "%s\n", static_key_foot);

  /* write number of bits */
  buf_printf (&nbits_head_text, "#\n# %d bit OpenVPN static key\n#\n", nbits);
  buf_write_string_file (&nbits_head_text, filename, fd);

  /* write key file, now formatted in out, to file */
  buf_write_string_file (&out, filename, fd);

  if (close (fd))
    msg (M_ERR, "Close error on shared secret file %s", filename);

  /* zero memory which held file content (memory will be freed by GC) */
  buf_clear (&out);

  /* pop our garbage collection level */
  gc_free (&gc);

  return nbits;
}