6fbf66fa |
/*
* 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.
* |
564a2109 |
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
6825182b |
* Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com> |
6fbf66fa |
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
1bda73a7 |
#include "syshead.h" |
6fbf66fa |
|
9b33b5a4 |
#ifdef ENABLE_CRYPTO |
6fbf66fa |
#include "crypto.h"
#include "error.h"
#include "misc.h"
#include "memdbg.h"
/*
* Encryption and Compression Routines.
*
* On entry, buf contains the input data and length.
* On exit, it should be set to the output data and length.
*
* If buf->len is <= 0 we should return
* If buf->len is set to 0 on exit it tells the caller to ignore the packet.
*
* work is a workspace buffer we are given of size BUF_SIZE.
* work may be used to return output data, or the input buffer
* may be modified and returned as output. If output data is
* returned in work, the data should start after FRAME_HEADROOM bytes
* of padding to leave room for downstream routines to prepend.
*
* Up to a total of FRAME_HEADROOM bytes may be prepended to the input buf
* by all routines (encryption, decryption, compression, and decompression).
*
* Note that the buf_prepend return will assert if we try to
* make a header bigger than FRAME_HEADROOM. This should not
* happen unless the frame parameters are wrong.
*/
#define CRYPT_ERROR(format) \
do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
|
11d21349 |
/**
* As memcmp(), but constant-time.
* Returns 0 when data is equal, non-zero otherwise.
*/
static int
memcmp_constant_time (const void *a, const void *b, size_t size) {
const uint8_t * a1 = a;
const uint8_t * b1 = b;
int ret = 0;
size_t i;
for (i = 0; i < size; i++) {
ret |= *a1++ ^ *b1++;
}
return ret;
}
|
6fbf66fa |
void
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)
{ |
b5738e5b |
uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH]; |
485c5f76 |
const int iv_size = cipher_ctx_iv_length (ctx->cipher); |
a4b27b64 |
const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); |
6fbf66fa |
int outlen;
|
a4b27b64 |
if (cipher_kt_mode_cbc(cipher_kt)) |
6fbf66fa |
{
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));
}
} |
a4b27b64 |
else if (cipher_kt_mode_ofb_cfb(cipher_kt)) |
6fbf66fa |
{
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 */ |
485c5f76 |
ASSERT (cipher_ctx_reset(ctx->cipher, iv_buf)); |
6fbf66fa |
/* Buffer overflow check */ |
485c5f76 |
if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher))) |
6fbf66fa |
{
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, |
485c5f76 |
cipher_ctx_block_size (ctx->cipher)); |
6fbf66fa |
goto err;
}
/* Encrypt packet ID, payload */ |
485c5f76 |
ASSERT (cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))); |
6fbf66fa |
work.len += outlen;
/* Flush the encryption buffer */ |
485c5f76 |
ASSERT(cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen)); |
6fbf66fa |
work.len += outlen; |
a4b27b64 |
/* For all CBC mode ciphers, check the last block is complete */
ASSERT (cipher_kt_mode (cipher_kt) != OPENVPN_MODE_CBC ||
outlen == iv_size); |
6fbf66fa |
/* 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)
{ |
e8c950f1 |
uint8_t *output = NULL; |
6fbf66fa |
|
e8c950f1 |
hmac_ctx_reset (ctx->hmac);
hmac_ctx_update (ctx->hmac, BPTR(&work), BLEN(&work));
output = buf_prepend (&work, hmac_ctx_size(ctx->hmac)); |
6fbf66fa |
ASSERT (output); |
e8c950f1 |
hmac_ctx_final (ctx->hmac, output); |
6fbf66fa |
}
*buf = work;
}
gc_free (&gc);
return;
|
330715f0 |
err:
crypto_clear_error(); |
6fbf66fa |
buf->len = 0;
gc_free (&gc);
return;
}
/*
* If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet.
*
* Set buf->len to 0 and return false on decrypt error.
*
* On success, buf is set to point to plaintext, true
* is returned.
*/
bool
openvpn_decrypt (struct buffer *buf, struct buffer work,
const struct crypto_options *opt,
const struct frame* frame)
{
static const char error_prefix[] = "Authenticate/Decrypt packet error";
struct gc_arena gc;
gc_init (&gc);
if (buf->len > 0 && opt->key_ctx_bi)
{
struct key_ctx *ctx = &opt->key_ctx_bi->decrypt;
struct packet_id_net pin;
bool have_pin = false;
/* Verify the HMAC */
if (ctx->hmac)
{
int hmac_len;
uint8_t local_hmac[MAX_HMAC_KEY_LENGTH]; /* HMAC of ciphertext computed locally */
|
e8c950f1 |
hmac_ctx_reset(ctx->hmac); |
6fbf66fa |
/* Assume the length of the input HMAC */ |
e8c950f1 |
hmac_len = hmac_ctx_size (ctx->hmac); |
6fbf66fa |
/* Authentication fails if insufficient data in packet for HMAC */
if (buf->len < hmac_len)
CRYPT_ERROR ("missing authentication info");
|
e8c950f1 |
hmac_ctx_update (ctx->hmac, BPTR (buf) + hmac_len, BLEN (buf) - hmac_len);
hmac_ctx_final (ctx->hmac, local_hmac); |
6fbf66fa |
/* Compare locally computed HMAC with packet HMAC */ |
11d21349 |
if (memcmp_constant_time (local_hmac, BPTR (buf), hmac_len)) |
6fbf66fa |
CRYPT_ERROR ("packet HMAC authentication failed");
ASSERT (buf_advance (buf, hmac_len));
}
/* Decrypt packet ID + payload */
if (ctx->cipher)
{ |
485c5f76 |
const int iv_size = cipher_ctx_iv_length (ctx->cipher); |
a4b27b64 |
const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); |
b5738e5b |
uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH]; |
6fbf66fa |
int outlen;
/* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
ASSERT (buf_init (&work, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_DECRYPT)));
/* use IV if user requested it */
CLEAR (iv_buf);
if (opt->flags & CO_USE_IV)
{
if (buf->len < iv_size)
CRYPT_ERROR ("missing IV info");
memcpy (iv_buf, BPTR (buf), iv_size);
ASSERT (buf_advance (buf, iv_size));
}
/* show the IV's initial state */
if (opt->flags & CO_USE_IV)
dmsg (D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc));
if (buf->len < 1)
CRYPT_ERROR ("missing payload");
/* ctx->cipher was already initialized with key & keylen */ |
485c5f76 |
if (!cipher_ctx_reset (ctx->cipher, iv_buf)) |
6fbf66fa |
CRYPT_ERROR ("cipher init failed");
/* Buffer overflow check (should never happen) */
if (!buf_safe (&work, buf->len))
CRYPT_ERROR ("buffer overflow");
/* Decrypt packet ID, payload */ |
485c5f76 |
if (!cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))) |
6fbf66fa |
CRYPT_ERROR ("cipher update failed");
work.len += outlen;
/* Flush the decryption buffer */ |
485c5f76 |
if (!cipher_ctx_final (ctx->cipher, BPTR (&work) + outlen, &outlen)) |
6fbf66fa |
CRYPT_ERROR ("cipher final failed");
work.len += outlen;
dmsg (D_PACKET_CONTENT, "DECRYPT TO: %s",
format_hex (BPTR (&work), BLEN (&work), 80, &gc));
/* Get packet ID from plaintext buffer or IV, depending on cipher mode */
{ |
a4b27b64 |
if (cipher_kt_mode_cbc(cipher_kt)) |
6fbf66fa |
{
if (opt->packet_id)
{
if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)))
CRYPT_ERROR ("error reading CBC packet-id");
have_pin = true;
}
} |
a4b27b64 |
else if (cipher_kt_mode_ofb_cfb(cipher_kt)) |
6fbf66fa |
{
struct buffer b;
ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */
ASSERT (opt->packet_id); /* for this mode. */
buf_set_read (&b, iv_buf, iv_size);
if (!packet_id_read (&pin, &b, true))
CRYPT_ERROR ("error reading CFB/OFB packet-id");
have_pin = true;
}
else /* We only support CBC, CFB, or OFB modes right now */
{
ASSERT (0);
}
}
}
else
{
work = *buf;
if (opt->packet_id)
{
if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)))
CRYPT_ERROR ("error reading packet-id");
have_pin = !BOOL_CAST (opt->flags & CO_IGNORE_PACKET_ID);
}
}
if (have_pin)
{
packet_id_reap_test (&opt->packet_id->rec);
if (packet_id_test (&opt->packet_id->rec, &pin))
{
packet_id_add (&opt->packet_id->rec, &pin);
if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM))
packet_id_persist_save_obj (opt->pid_persist, opt->packet_id);
}
else
{
if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS))
msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings",
error_prefix, packet_id_net_print (&pin, true, &gc));
goto error_exit;
}
}
*buf = work;
}
gc_free (&gc);
return true;
error_exit: |
330715f0 |
crypto_clear_error(); |
6fbf66fa |
buf->len = 0;
gc_free (&gc);
return false;
}
/*
* How many bytes will we add to frame buffer for a given
* set of crypto options?
*/
void
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)
{ |
669f898b |
size_t crypto_overhead = 0;
if (packet_id)
crypto_overhead += packet_id_size (packet_id_long_form);
if (cipher_defined)
{
if (use_iv)
crypto_overhead += cipher_kt_iv_size (kt->cipher);
if (cipher_kt_mode_cbc (kt->cipher))
/* worst case padding expansion */
crypto_overhead += cipher_kt_block_size (kt->cipher);
}
crypto_overhead += kt->hmac_length;
frame_add_to_extra_frame (frame, crypto_overhead);
msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for crypto by %zu bytes",
__func__, crypto_overhead); |
6fbf66fa |
}
/*
* Build a struct key_type.
*/
void
init_key_type (struct key_type *kt, const char *ciphername,
bool ciphername_defined, const char *authname,
bool authname_defined, int keysize,
bool cfb_ofb_allowed, bool warn)
{
CLEAR (*kt);
if (ciphername && ciphername_defined)
{ |
f499b921 |
kt->cipher = cipher_kt_get (translate_cipher_name_from_openvpn(ciphername)); |
670f9dd9 |
kt->cipher_length = cipher_kt_key_size (kt->cipher); |
6fbf66fa |
if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH)
kt->cipher_length = keysize;
/* check legal cipher mode */
{ |
a4b27b64 |
if (!(cipher_kt_mode_cbc(kt->cipher) |
c353af2f |
#ifdef ENABLE_OFB_CFB_MODE |
a4b27b64 |
|| (cfb_ofb_allowed && cipher_kt_mode_ofb_cfb(kt->cipher)) |
6fbf66fa |
#endif
))
msg (M_FATAL, "Cipher '%s' mode not supported", ciphername);
}
}
else
{
if (warn)
msg (M_WARN, "******* WARNING *******: null cipher specified, no encryption will be used");
}
if (authname && authname_defined)
{ |
902f674e |
kt->digest = md_kt_get (authname);
kt->hmac_length = md_kt_size (kt->digest); |
6fbf66fa |
}
else
{
if (warn)
msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used");
}
}
/* given a key and key_type, build a key_ctx */
void
init_key_ctx (struct key_ctx *ctx, struct key *key,
const struct key_type *kt, int enc,
const char *prefix)
{ |
0d4ec3d8 |
struct gc_arena gc = gc_new (); |
6fbf66fa |
CLEAR (*ctx);
if (kt->cipher && kt->cipher_length > 0)
{ |
0d4ec3d8 |
|
485c5f76 |
ALLOC_OBJ(ctx->cipher, cipher_ctx_t);
cipher_ctx_init (ctx->cipher, key->cipher, kt->cipher_length, |
0d4ec3d8 |
kt->cipher, enc);
msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key",
prefix,
cipher_kt_name(kt->cipher),
kt->cipher_length *8);
dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
format_hex (key->cipher, kt->cipher_length, 0, &gc));
dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d",
prefix,
cipher_kt_block_size(kt->cipher),
cipher_kt_iv_size(kt->cipher)); |
6fbf66fa |
}
if (kt->digest && kt->hmac_length > 0)
{ |
e8c950f1 |
ALLOC_OBJ(ctx->hmac, hmac_ctx_t); |
62242ed2 |
hmac_ctx_init (ctx->hmac, key->hmac, kt->hmac_length, kt->digest);
msg (D_HANDSHAKE,
"%s: Using %d bit message hash '%s' for HMAC authentication",
prefix, md_kt_size(kt->digest) * 8, md_kt_name(kt->digest));
dmsg (D_SHOW_KEYS, "%s: HMAC KEY: %s", prefix,
format_hex (key->hmac, kt->hmac_length, 0, &gc));
dmsg (D_CRYPTO_DEBUG, "%s: HMAC size=%d block_size=%d",
prefix,
md_kt_size(kt->digest),
hmac_ctx_size(ctx->hmac));
|
6fbf66fa |
} |
0d4ec3d8 |
gc_free (&gc); |
6fbf66fa |
}
void
free_key_ctx (struct key_ctx *ctx)
{
if (ctx->cipher)
{ |
485c5f76 |
cipher_ctx_cleanup(ctx->cipher);
free(ctx->cipher); |
6fbf66fa |
ctx->cipher = NULL;
}
if (ctx->hmac)
{ |
e8c950f1 |
hmac_ctx_cleanup(ctx->hmac);
free(ctx->hmac); |
6fbf66fa |
ctx->hmac = NULL;
}
}
void
free_key_ctx_bi (struct key_ctx_bi *ctx)
{
free_key_ctx(&ctx->encrypt);
free_key_ctx(&ctx->decrypt);
}
static bool
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;
}
/*
* Make sure that cipher key is a valid key for current key_type.
*/
bool
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.
*/
{ |
183c3d19 |
const int ndc = key_des_num_cblocks (kt->cipher); |
6fbf66fa |
if (ndc) |
183c3d19 |
return key_des_check (key->cipher, kt->cipher_length, ndc); |
6fbf66fa |
else
return true;
}
}
return true;
}
/*
* Make safe mutations to key to ensure it is valid,
* such as ensuring correct parity on DES keys.
*
* This routine cannot guarantee it will generate a good
* key. You must always call check_key after this routine
* to make sure.
*/
void
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 |
183c3d19 |
const int ndc = key_des_num_cblocks (kt->cipher); |
6fbf66fa |
if (ndc) |
183c3d19 |
key_des_fixup (key->cipher, kt->cipher_length, ndc); |
6fbf66fa |
#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);
}
void
check_replay_iv_consistency (const struct key_type *kt, bool packet_id, bool use_iv)
{ |
a4b27b64 |
ASSERT(kt); |
6fbf66fa |
|
a4b27b64 |
if (cipher_kt_mode_ofb_cfb(kt->cipher) && !(packet_id && use_iv))
msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB mode cipher"); |
6fbf66fa |
}
/*
* Generate a random key. If key_type is provided, make
* sure generated key is valid for key_type.
*/
void
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;
} |
6825182b |
if (!rand_bytes (key->cipher, cipher_len)
|| !rand_bytes (key->hmac, hmac_len)) |
6fbf66fa |
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);
}
/*
* Print key material
*/
void
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);
}
void
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; |
6825182b |
ASSERT (rand_bytes (BPTR (&src), BLEN (&src))); |
6fbf66fa |
/* 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);
}
void
get_tls_handshake_key (const struct key_type *key_type,
struct key_ctx_bi *ctx,
const char *passphrase_file, |
e5d281cf |
const int key_direction, |
c959fc74 |
const unsigned int flags) |
6fbf66fa |
{
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;
|
c959fc74 |
if (flags & GHK_INLINE) |
6fbf66fa |
{ |
c959fc74 |
/* 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"); |
6fbf66fa |
}
else |
c959fc74 |
{
/* first try to parse as an OpenVPN static key file */
read_key_file (&key2, passphrase_file, 0); |
6fbf66fa |
|
c959fc74 |
/* succeeded? */
if (key2.n == 2)
{
msg (M_INFO,
"Control Channel Authentication: using '%s' as a " PACKAGE_NAME " static key file",
passphrase_file);
}
else
{
CLEAR (key2); |
6fbf66fa |
|
8a95f62e |
/* failed, now bail out */ |
6fbf66fa |
|
8a95f62e |
msg (M_ERR,
"Control Channel Authentication: File '%s' does not have OpenVPN Static Key format. "
"Using free-form passphrase file is not supported anymore", |
c959fc74 |
passphrase_file);
}
} |
6fbf66fa |
/* handle key direction */
|
e5d281cf |
key_direction_state_init (&kds, key_direction); |
6fbf66fa |
must_have_n_keys (passphrase_file, "tls-auth", &key2, kds.need_keys);
/* initialize hmac key in both directions */
|
b5738e5b |
init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], &kt, OPENVPN_OP_ENCRYPT, |
6fbf66fa |
"Outgoing Control Channel Authentication"); |
b5738e5b |
init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, OPENVPN_OP_DECRYPT, |
6fbf66fa |
"Incoming Control Channel Authentication");
CLEAR (key2);
}
else
{
CLEAR (*ctx);
}
}
/* header and footer for static key file */ |
74fce85e |
static const char static_key_head[] = "-----BEGIN OpenVPN Static key V1-----";
static const char static_key_foot[] = "-----END OpenVPN Static key V1-----"; |
6fbf66fa |
static const char printable_char_fmt[] =
"Non-Hex character ('%c') found at line %d in key file '%s' (%d/%d/%d bytes found/min/max)";
static const char unprintable_char_fmt[] =
"Non-Hex, unprintable character (0x%02x) found at line %d in key file '%s' (%d/%d/%d bytes found/min/max)";
/* read key from file */ |
c959fc74 |
|
6fbf66fa |
void |
c959fc74 |
read_key_file (struct key2 *key2, const char *file, const unsigned int flags) |
6fbf66fa |
{
struct gc_arena gc = gc_new (); |
c959fc74 |
struct buffer in; |
6fbf66fa |
int fd, size;
uint8_t hex_byte[3] = {0, 0, 0}; |
c959fc74 |
const char *error_filename = file; |
6fbf66fa |
/* parse info */ |
dcc0b244 |
const unsigned char *cp; |
6fbf66fa |
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);
|
c959fc74 |
/*
* 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); |
14a131ac |
fd = platform_open (file, O_RDONLY, 0); |
c959fc74 |
if (fd == -1)
msg (M_ERR, "Cannot open file key file '%s'", file);
size = read (fd, in.data, in.capacity); |
24f8f368 |
if (size < 0)
msg (M_FATAL, "Read error on key file ('%s')", file); |
c959fc74 |
if (size == in.capacity)
msg (M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity);
close (fd);
} |
6fbf66fa |
|
dcc0b244 |
cp = (unsigned char *)in.data; |
24f8f368 |
while (size > 0) |
6fbf66fa |
{ |
dcc0b244 |
const unsigned char c = *cp; |
6fbf66fa |
#if 0 |
74fce85e |
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); |
6fbf66fa |
#endif
|
c959fc74 |
if (c == '\n')
{
line_index = match = 0;
++line_num;
}
else
{
/* first char of new line */
if (!line_index) |
6fbf66fa |
{ |
c959fc74 |
/* 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; |
6fbf66fa |
} |
c959fc74 |
/* compare read chars with header line */
if (state == PARSE_INITIAL) |
6fbf66fa |
{ |
c959fc74 |
if (line_index < hlen && c == static_key_head[line_index]) |
6fbf66fa |
{ |
c959fc74 |
if (++match == hlen)
state = PARSE_HEAD; |
6fbf66fa |
} |
c959fc74 |
} |
6fbf66fa |
|
c959fc74 |
/* compare read chars with footer line */
if (state == PARSE_FOOT)
{
if (line_index < flen && c == static_key_foot[line_index]) |
6fbf66fa |
{ |
c959fc74 |
if (++match == flen)
state = PARSE_FINISHED; |
6fbf66fa |
} |
c959fc74 |
} |
6fbf66fa |
|
c959fc74 |
/* reading key */
if (state == PARSE_DATA)
{
if (isxdigit(c)) |
6fbf66fa |
{ |
c959fc74 |
ASSERT (hb_index >= 0 && hb_index < 2);
hex_byte[hb_index++] = c;
if (hb_index == 2) |
6fbf66fa |
{ |
c959fc74 |
unsigned int u;
ASSERT(sscanf((const char *)hex_byte, "%x", &u) == 1);
*out++ = u;
hb_index = 0;
if (++count == keylen)
state = PARSE_DATA_COMPLETE; |
6fbf66fa |
}
} |
c959fc74 |
else if (isspace(c))
;
else |
6fbf66fa |
{ |
c959fc74 |
msg (M_FATAL,
(isprint (c) ? printable_char_fmt : unprintable_char_fmt),
c, line_num, error_filename, count, onekeylen, keylen); |
6fbf66fa |
}
} |
c959fc74 |
++line_index; |
6fbf66fa |
} |
c959fc74 |
++cp;
--size; |
6fbf66fa |
}
/*
* 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));
|
c959fc74 |
if (flags & RKF_MUST_SUCCEED) |
6fbf66fa |
{
if (!key2->n) |
74fce85e |
msg (M_FATAL, "Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max)", |
c959fc74 |
error_filename, count, onekeylen, keylen); |
6fbf66fa |
if (state != PARSE_FINISHED)
msg (M_FATAL, "Footer text not found in file '%s' (%d/%d/%d bytes found/min/max)", |
c959fc74 |
error_filename, count, onekeylen, keylen); |
6fbf66fa |
}
|
1f4309ae |
/* zero file read buffer if not an inline file */
if (!(flags & RKF_INLINE))
buf_clear (&in); |
6fbf66fa |
if (key2->n) |
c959fc74 |
warn_if_group_others_accessible (error_filename); |
6fbf66fa |
#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);
}
/*
* Write key to file, return number of random bits
* written.
*/
int
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 */ |
14a131ac |
fd = platform_open (filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); |
6fbf66fa |
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 */ |
74fce85e |
buf_printf (&nbits_head_text, "#\n# %d bit OpenVPN static key\n#\n", nbits); |
6fbf66fa |
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;
}
void
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
}
}
int
ascii2keydirection (int msglevel, const char *str)
{
if (!str)
return KEY_DIRECTION_BIDIRECTIONAL;
else if (!strcmp (str, "0"))
return KEY_DIRECTION_NORMAL;
else if (!strcmp (str, "1"))
return KEY_DIRECTION_INVERSE;
else
{
msg (msglevel, "Unknown key direction '%s' -- must be '0' or '1'", str);
return -1;
}
return KEY_DIRECTION_BIDIRECTIONAL; /* NOTREACHED */
}
const char *
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 */
}
void
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);
}
}
void
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);
}
}
/* given a key and key_type, write key to buffer */
bool
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;
}
/*
* Given a key_type and buffer, read key from buffer.
* Return: 1 on success
* -1 read failure
* 0 on key length mismatch
*/
int
read_key (struct key *key, const struct key_type *kt, struct buffer *buf)
{
uint8_t cipher_length;
uint8_t hmac_length;
CLEAR (*key);
if (!buf_read (buf, &cipher_length, 1))
goto read_err;
if (!buf_read (buf, &hmac_length, 1))
goto read_err;
if (!buf_read (buf, key->cipher, cipher_length))
goto read_err;
if (!buf_read (buf, key->hmac, hmac_length))
goto read_err;
if (cipher_length != kt->cipher_length || hmac_length != kt->hmac_length)
goto key_len_err;
return 1;
read_err:
msg (D_TLS_ERRORS, "TLS Error: error reading key from remote");
return -1;
key_len_err:
msg (D_TLS_ERRORS,
"TLS Error: key length mismatch, local cipher/hmac %d/%d, remote cipher/hmac %d/%d",
kt->cipher_length, kt->hmac_length, cipher_length, hmac_length);
return 0;
}
/*
* Random number functions, used in cases where we want
* reasonably strong cryptographic random number generation
* without depleting our entropy pool. Used for random
* IV values and a number of other miscellaneous tasks.
*/
|
902f674e |
static uint8_t *nonce_data = NULL; /* GLOBAL */
static const md_kt_t *nonce_md = NULL; /* GLOBAL */
static int nonce_secret_len = 0; /* GLOBAL */ |
6fbf66fa |
|
557624e0 |
/* Reset the nonce value, also done periodically to refresh entropy */
static void
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
}
|
03bfb228 |
void
prng_init (const char *md_name, const int nonce_secret_len_parm)
{
prng_uninit (); |
902f674e |
nonce_md = md_name ? md_kt_get (md_name) : NULL; |
03bfb228 |
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;
{ |
902f674e |
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); |
03bfb228 |
nonce_data = (uint8_t*) malloc (size);
check_malloc_return (nonce_data); |
557624e0 |
prng_reset_nonce(); |
03bfb228 |
}
}
} |
6fbf66fa |
void |
03bfb228 |
prng_uninit (void) |
6fbf66fa |
{ |
03bfb228 |
free (nonce_data);
nonce_data = NULL;
nonce_md = NULL;
nonce_secret_len = 0; |
6fbf66fa |
}
void
prng_bytes (uint8_t *output, int len)
{ |
be960aad |
static size_t processed = 0; |
557624e0 |
|
03bfb228 |
if (nonce_md) |
6fbf66fa |
{ |
902f674e |
const int md_size = md_kt_size (nonce_md); |
03bfb228 |
while (len > 0)
{
const int blen = min_int (len, md_size); |
d5f44617 |
md_full(nonce_md, nonce_data, md_size + nonce_secret_len, nonce_data); |
03bfb228 |
memcpy (output, nonce_data, blen);
output += blen;
len -= blen; |
557624e0 |
/* Ensure that random data is reset regularly */
processed += blen;
if(processed > PRNG_NONCE_RESET_BYTES) {
prng_reset_nonce();
processed = 0;
} |
03bfb228 |
} |
6fbf66fa |
} |
03bfb228 |
else |
6825182b |
rand_bytes (output, len); |
6fbf66fa |
}
/* an analogue to the random() function, but use prng_bytes */
long int
get_random()
{
long int l;
prng_bytes ((unsigned char *)&l, sizeof(l));
if (l < 0)
l = -l;
return l;
}
|
9b33b5a4 |
#endif /* ENABLE_CRYPTO */ |