summaryrefslogtreecommitdiff
path: root/src/openvpn/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/crypto.c')
-rw-r--r--src/openvpn/crypto.c344
1 files changed, 213 insertions, 131 deletions
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 7e7dead..3a0bfbe 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -30,8 +30,6 @@
#include "syshead.h"
-#ifdef ENABLE_CRYPTO
-
#include "crypto.h"
#include "error.h"
#include "integer.h"
@@ -66,7 +64,6 @@ static void
openvpn_encrypt_aead(struct buffer *buf, struct buffer work,
struct crypto_options *opt)
{
-#ifdef HAVE_AEAD_CIPHER_MODES
struct gc_arena gc;
int outlen = 0;
const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
@@ -77,7 +74,6 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work,
/* IV, packet-ID and implicit IV required for this mode. */
ASSERT(ctx->cipher);
ASSERT(cipher_kt_mode_aead(cipher_kt));
- ASSERT(opt->flags & CO_USE_IV);
ASSERT(packet_id_initialized(&opt->packet_id));
gc_init(&gc);
@@ -155,9 +151,6 @@ err:
buf->len = 0;
gc_free(&gc);
return;
-#else /* HAVE_AEAD_CIPHER_MODES */
- ASSERT(0);
-#endif /* ifdef HAVE_AEAD_CIPHER_MODES */
}
static void
@@ -192,10 +185,7 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work,
if (cipher_kt_mode_cbc(cipher_kt))
{
/* generate pseudo-random IV */
- if (opt->flags & CO_USE_IV)
- {
- prng_bytes(iv_buf, iv_size);
- }
+ prng_bytes(iv_buf, iv_size);
/* Put packet ID in plaintext buffer */
if (packet_id_initialized(&opt->packet_id)
@@ -211,8 +201,7 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work,
{
struct buffer b;
- /* IV and packet-ID required for this mode. */
- ASSERT(opt->flags & CO_USE_IV);
+ /* packet-ID required for this mode. */
ASSERT(packet_id_initialized(&opt->packet_id));
buf_set_write(&b, iv_buf, iv_size);
@@ -224,11 +213,8 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work,
}
/* set the IV pseudo-randomly */
- if (opt->flags & CO_USE_IV)
- {
- ASSERT(buf_write(&work, iv_buf, iv_size));
- dmsg(D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc));
- }
+ ASSERT(buf_write(&work, iv_buf, iv_size));
+ 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));
@@ -358,20 +344,19 @@ crypto_check_replay(struct crypto_options *opt,
return ret;
}
-/*
- * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet.
+/**
+ * Unwrap (authenticate, decrypt and check replay protection) AEAD-mode data
+ * channel packets.
*
* Set buf->len to 0 and return false on decrypt error.
*
- * On success, buf is set to point to plaintext, true
- * is returned.
+ * On success, buf is set to point to plaintext, true is returned.
*/
static bool
openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
struct crypto_options *opt, const struct frame *frame,
const uint8_t *ad_start)
{
-#ifdef HAVE_AEAD_CIPHER_MODES
static const char error_prefix[] = "AEAD Decrypt error";
struct packet_id_net pin = { 0 };
const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt;
@@ -398,7 +383,6 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
/* IV and Packet ID required for this mode */
ASSERT(packet_id_initialized(&opt->packet_id));
- ASSERT(opt->flags & CO_USE_IV);
/* Combine IV from explicit part from packet and implicit part from context */
{
@@ -439,13 +423,6 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
tag_ptr = BPTR(buf);
ASSERT(buf_advance(buf, tag_size));
dmsg(D_PACKET_CONTENT, "DECRYPT MAC: %s", format_hex(tag_ptr, tag_size, 0, &gc));
-#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10001040L
- /* OpenSSL <= 1.0.1c bug requires set tag before processing ciphertext */
- if (!EVP_CIPHER_CTX_ctrl(ctx->cipher, EVP_CTRL_GCM_SET_TAG, tag_size, tag_ptr))
- {
- CRYPT_ERROR("setting tag failed");
- }
-#endif
if (buf->len < 1)
{
@@ -500,19 +477,15 @@ error_exit:
buf->len = 0;
gc_free(&gc);
return false;
-#else /* HAVE_AEAD_CIPHER_MODES */
- ASSERT(0);
- return false;
-#endif /* ifdef HAVE_AEAD_CIPHER_MODES */
}
/*
- * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet.
+ * Unwrap (authenticate, decrypt and check replay protection) CBC, OFB or CFB
+ * mode data channel packets.
*
* Set buf->len to 0 and return false on decrypt error.
*
- * On success, buf is set to point to plaintext, true
- * is returned.
+ * On success, buf is set to point to plaintext, true is returned.
*/
static bool
openvpn_decrypt_v1(struct buffer *buf, struct buffer work,
@@ -572,22 +545,14 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work,
/* 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 */
- 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)
+ /* read the IV from the packet */
+ if (buf->len < iv_size)
{
- dmsg(D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc));
+ CRYPT_ERROR("missing IV info");
}
+ memcpy(iv_buf, BPTR(buf), iv_size);
+ ASSERT(buf_advance(buf, iv_size));
+ dmsg(D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc));
if (buf->len < 1)
{
@@ -640,8 +605,7 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work,
{
struct buffer b;
- /* IV and packet-ID required for this mode. */
- ASSERT(opt->flags & CO_USE_IV);
+ /* packet-ID required for this mode. */
ASSERT(packet_id_initialized(&opt->packet_id));
buf_set_read(&b, iv_buf, iv_size);
@@ -717,7 +681,6 @@ openvpn_decrypt(struct buffer *buf, struct buffer work,
void
crypto_adjust_frame_parameters(struct frame *frame,
const struct key_type *kt,
- bool use_iv,
bool packet_id,
bool packet_id_long_form)
{
@@ -730,10 +693,7 @@ crypto_adjust_frame_parameters(struct frame *frame,
if (kt->cipher)
{
- if (use_iv)
- {
- crypto_overhead += cipher_kt_iv_size(kt->cipher);
- }
+ crypto_overhead += cipher_kt_iv_size(kt->cipher);
if (cipher_kt_mode_aead(kt->cipher))
{
@@ -760,6 +720,20 @@ crypto_max_overhead(void)
+max_int(OPENVPN_MAX_HMAC_SIZE, OPENVPN_AEAD_TAG_LENGTH);
}
+static void
+warn_insecure_key_type(const char *ciphername, const cipher_kt_t *cipher)
+{
+ if (cipher_kt_insecure(cipher))
+ {
+ msg(M_WARN, "WARNING: INSECURE cipher (%s) with block size less than 128"
+ " bit (%d bit). This allows attacks like SWEET32. Mitigate by "
+ "using a --cipher with a larger block size (e.g. AES-256-CBC). "
+ "Support for these insecure ciphers will be removed in "
+ "OpenVPN 2.6.",
+ ciphername, cipher_kt_block_size(cipher)*8);
+ }
+}
+
/*
* Build a struct key_type.
*/
@@ -775,7 +749,7 @@ init_key_type(struct key_type *kt, const char *ciphername,
CLEAR(*kt);
if (strcmp(ciphername, "none") != 0)
{
- kt->cipher = cipher_kt_get(translate_cipher_name_from_openvpn(ciphername));
+ kt->cipher = cipher_kt_get(ciphername);
if (!kt->cipher)
{
msg(M_FATAL, "Cipher %s not supported", ciphername);
@@ -803,6 +777,10 @@ init_key_type(struct key_type *kt, const char *ciphername,
{
msg(M_FATAL, "Cipher '%s' not allowed: block size too big.", ciphername);
}
+ if (warn)
+ {
+ warn_insecure_key_type(ciphername, kt->cipher);
+ }
}
else
{
@@ -855,9 +833,10 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key,
cipher_ctx_init(ctx->cipher, key->cipher, kt->cipher_length,
kt->cipher, enc);
+ const char *ciphername = cipher_kt_name(kt->cipher);
msg(D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key",
prefix,
- translate_cipher_name_to_openvpn(cipher_kt_name(kt->cipher)),
+ ciphername,
kt->cipher_length *8);
dmsg(D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
@@ -865,13 +844,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key,
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));
- if (cipher_kt_block_size(kt->cipher) < 128/8)
- {
- msg(M_WARN, "WARNING: INSECURE cipher with block size less than 128"
- " bit (%d bit). This allows attacks like SWEET32. Mitigate by "
- "using a --cipher with a larger block size (e.g. AES-256-CBC).",
- cipher_kt_block_size(kt->cipher)*8);
- }
+ warn_insecure_key_type(ciphername, kt->cipher);
}
if (kt->digest && kt->hmac_length > 0)
{
@@ -943,10 +916,12 @@ 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;
}
@@ -1025,15 +1000,14 @@ fixup_key(struct key *key, const struct key_type *kt)
}
void
-check_replay_iv_consistency(const struct key_type *kt, bool packet_id, bool use_iv)
+check_replay_consistency(const struct key_type *kt, bool packet_id)
{
ASSERT(kt);
- if (!(packet_id && use_iv) && (cipher_kt_mode_ofb_cfb(kt->cipher)
- || cipher_kt_mode_aead(kt->cipher)))
+ if (!packet_id && (cipher_kt_mode_ofb_cfb(kt->cipher)
+ || cipher_kt_mode_aead(kt->cipher)))
{
- msg(M_FATAL, "--no-replay or --no-iv cannot be used with a CFB, OFB or "
- "AEAD mode cipher");
+ msg(M_FATAL, "--no-replay cannot be used with a CFB, OFB or AEAD mode cipher");
}
}
@@ -1123,7 +1097,6 @@ test_crypto(struct crypto_options *co, struct frame *frame)
/* init work */
ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
-#ifdef HAVE_AEAD_CIPHER_MODES
/* init implicit IV */
{
const cipher_kt_t *cipher =
@@ -1145,7 +1118,6 @@ test_crypto(struct crypto_options *co, struct frame *frame)
co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len;
}
}
-#endif /* ifdef HAVE_AEAD_CIPHER_MODES */
msg(M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode.");
for (i = 1; i <= TUN_MTU_SIZE(frame); ++i)
@@ -1196,27 +1168,38 @@ test_crypto(struct crypto_options *co, struct frame *frame)
gc_free(&gc);
}
+const char *
+print_key_filename(const char *str, bool is_inline)
+{
+ if (is_inline)
+ {
+ return "[[INLINE]]";
+ }
+
+ return np(str);
+}
+
void
crypto_read_openvpn_key(const struct key_type *key_type,
- struct key_ctx_bi *ctx, const char *key_file, const char *key_inline,
- const int key_direction, const char *key_name, const char *opt_name)
+ struct key_ctx_bi *ctx, const char *key_file,
+ bool key_inline, const int key_direction,
+ const char *key_name, const char *opt_name)
{
struct key2 key2;
struct key_direction_state kds;
+ unsigned int flags = RKF_MUST_SUCCEED;
if (key_inline)
{
- read_key_file(&key2, key_inline, RKF_MUST_SUCCEED|RKF_INLINE);
- }
- else
- {
- read_key_file(&key2, key_file, RKF_MUST_SUCCEED);
+ flags |= RKF_INLINE;
}
+ read_key_file(&key2, key_file, flags);
if (key2.n != 2)
{
msg(M_ERR, "File '%s' does not have OpenVPN Static Key format. Using "
- "free-form passphrase file is not supported anymore.", key_file);
+ "free-form passphrase file is not supported anymore.",
+ print_key_filename(key_file, key_inline));
}
/* check for and fix highly unlikely key problems */
@@ -1248,9 +1231,8 @@ 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;
+ int size;
uint8_t hex_byte[3] = {0, 0, 0};
- const char *error_filename = file;
/* parse info */
const unsigned char *cp;
@@ -1288,26 +1270,16 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags)
{
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 key file '%s'", file);
- }
- size = read(fd, in.data, in.capacity);
- if (size < 0)
+ in = buffer_read_from_file(file, &gc);
+ if (!buf_valid(&in))
{
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);
+
+ size = in.len;
}
cp = (unsigned char *)in.data;
@@ -1393,7 +1365,9 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags)
{
msg(M_FATAL,
(isprint(c) ? printable_char_fmt : unprintable_char_fmt),
- c, line_num, error_filename, count, onekeylen, keylen);
+ c, line_num,
+ print_key_filename(file, flags & RKF_INLINE), count,
+ onekeylen, keylen);
}
}
++line_index;
@@ -1414,13 +1388,15 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags)
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);
+ print_key_filename(file, flags & RKF_INLINE), 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);
+ print_key_filename(file, flags & RKF_INLINE), count, onekeylen,
+ keylen);
}
}
@@ -1453,36 +1429,24 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags)
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;
+ int nbits = nkeys * sizeof(struct key) * 8;
/* 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);
- }
-
+ /* write header */
+ buf_printf(&out, "#\n# %d bit OpenVPN static key\n#\n", nbits);
buf_printf(&out, "%s\n", static_key_head);
- for (i = 0; i < nkeys; ++i)
+ for (int i = 0; i < nkeys; ++i)
{
struct key key;
char *fmt;
@@ -1498,9 +1462,6 @@ write_key_file(const int nkeys, const char *filename)
"\n",
&gc);
- /* increment random bits counter */
- nbits += sizeof(key) * 8;
-
/* write to holding buffer */
buf_printf(&out, "%s\n", fmt);
@@ -1511,16 +1472,15 @@ write_key_file(const int nkeys, const char *filename)
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 to stdout if no filename given */
+ if (!filename || strcmp(filename, "")==0)
+ {
+ printf("%.*s\n", BLEN(&out), BPTR(&out));
+ }
/* write key file, now formatted in out, to file */
- buf_write_string_file(&out, filename, fd);
-
- if (close(fd))
+ else if (!buffer_write_file(filename, &out))
{
- msg(M_ERR, "Close error on shared secret file %s", filename);
+ nbits = -1;
}
/* zero memory which held file content (memory will be freed by GC) */
@@ -1540,7 +1500,7 @@ must_have_n_keys(const char *filename, const char *option, const struct key2 *ke
#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);
+ 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
}
}
@@ -1748,7 +1708,9 @@ prng_reset_nonce(void)
{
int i;
for (i = 0; i < size; ++i)
+ {
nonce_data[i] = (uint8_t) i;
+ }
}
#endif
}
@@ -1825,6 +1787,33 @@ get_random(void)
return l;
}
+void
+print_cipher(const cipher_kt_t *cipher)
+{
+ const char *var_key_size = cipher_kt_var_key_size(cipher) ?
+ " by default" : "";
+
+ printf("%s (%d bit key%s, ",
+ cipher_kt_name(cipher),
+ cipher_kt_key_size(cipher) * 8, var_key_size);
+
+ if (cipher_kt_block_size(cipher) == 1)
+ {
+ printf("stream cipher");
+ }
+ else
+ {
+ printf("%d bit block", cipher_kt_block_size(cipher) * 8);
+ }
+
+ if (!cipher_kt_mode_cbc(cipher))
+ {
+ printf(", TLS client/server mode only");
+ }
+
+ printf(")\n");
+}
+
static const cipher_name_pair *
get_cipher_name_pair(const char *cipher_name)
{
@@ -1872,4 +1861,97 @@ translate_cipher_name_to_openvpn(const char *cipher_name)
return pair->openvpn_name;
}
-#endif /* ENABLE_CRYPTO */
+void
+write_pem_key_file(const char *filename, const char *pem_name)
+{
+ 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(pem_name, &server_key_pem,
+ &server_key_buf, &gc))
+ {
+ msg(M_WARN, "ERROR: could not PEM-encode key");
+ goto cleanup;
+ }
+
+ if (!filename || strcmp(filename, "")==0)
+ {
+ printf("%.*s", BLEN(&server_key_pem), BPTR(&server_key_pem));
+ }
+ else if (!buffer_write_file(filename, &server_key_pem))
+ {
+ msg(M_ERR, "ERROR: could not write key file");
+ goto cleanup;
+ }
+
+cleanup:
+ secure_memzero(&server_key, sizeof(server_key));
+ buf_clear(&server_key_pem);
+ gc_free(&gc);
+ return;
+}
+
+bool
+generate_ephemeral_key(struct buffer *key, const char *key_name)
+{
+ const int len = BCAP(key);
+
+ msg(M_INFO, "Using random %s.", key_name);
+
+ if (!rand_bytes(BEND(key), len))
+ {
+ msg(M_WARN, "ERROR: could not generate random key");
+ return false;
+ }
+
+ buf_inc_len(key, len);
+
+ return true;
+}
+
+bool
+read_pem_key_file(struct buffer *key, const char *pem_name,
+ const char *key_file, bool key_inline)
+{
+ bool ret = false;
+ struct buffer key_pem = { 0 };
+ struct gc_arena gc = gc_new();
+
+ if (!key_inline)
+ {
+ key_pem = buffer_read_from_file(key_file, &gc);
+ if (!buf_valid(&key_pem))
+ {
+ msg(M_WARN, "ERROR: failed to read %s file (%s)",
+ pem_name, key_file);
+ goto cleanup;
+ }
+ }
+ else
+ {
+ buf_set_read(&key_pem, (const void *)key_file, strlen(key_file) + 1);
+ }
+
+ if (!crypto_pem_decode(pem_name, key, &key_pem))
+ {
+ msg(M_WARN, "ERROR: %s pem decode failed", pem_name);
+ goto cleanup;
+ }
+
+ ret = true;
+cleanup:
+ if (!key_inline)
+ {
+ buf_clear(&key_pem);
+ }
+ gc_free(&gc);
+ return ret;
+}