Skip to content

Commit

Permalink
Implement methods to generate and manage OpenVPN Epoch keys
Browse files Browse the repository at this point in the history
This implements functions that allow these keys to be generated and
managed. It does not yet implement using them for the data channel.

Change-Id: Id7d6a576ca8c9560cb2dfae82fc62175820e9b80
Signed-off-by: Arne Schwabe <[email protected]>
Acked-by: MaxF <[email protected]>
Message-Id: <[email protected]>
URL: https://www.mail-archive.com/[email protected]/msg30390.html
Signed-off-by: Gert Doering <[email protected]>
  • Loading branch information
schwabe authored and cron2 committed Jan 9, 2025
1 parent bc62a9a commit 92adbc8
Show file tree
Hide file tree
Showing 8 changed files with 806 additions and 22 deletions.
25 changes: 21 additions & 4 deletions src/openvpn/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -916,14 +916,27 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, const struct key_parameters *key
if (cipher_ctx_mode_aead(ctx->cipher))
{
size_t impl_iv_len = 0;
size_t impl_iv_offset = 0;
ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
ASSERT(impl_iv_len + sizeof(packet_id_type) <= OPENVPN_MAX_IV_LENGTH);

/* Epoch keys use XOR of full IV length with the packet id to generate
* IVs. Old data format uses concatenation instead (XOR with 0 for the
* first 4 bytes (sizeof(packet_id_type) */
if (key->epoch)
{
impl_iv_len = cipher_ctx_iv_length(ctx->cipher);
impl_iv_offset = 0;
}
else
{
impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
impl_iv_offset = sizeof(packet_id_type);
}
ASSERT(impl_iv_offset + impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
ASSERT(impl_iv_len <= MAX_HMAC_KEY_LENGTH);
ASSERT(impl_iv_len <= key->hmac_size);
CLEAR(ctx->implicit_iv);
/* The first bytes of the IV are filled with the packet id */
memcpy(ctx->implicit_iv + sizeof(packet_id_type), key->hmac, impl_iv_len);
memcpy(ctx->implicit_iv + impl_iv_offset, key->hmac, impl_iv_len);
}
}

Expand Down Expand Up @@ -972,6 +985,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key_parameters *key,
hmac_ctx_size(ctx->hmac));

}
ctx->epoch = key->epoch;
gc_free(&gc);
}

Expand All @@ -984,6 +998,7 @@ init_key_bi_ctx_send(struct key_ctx *ctx, const struct key_parameters *key_param
snprintf(log_prefix, sizeof(log_prefix), "Outgoing %s", name);
init_key_ctx(ctx, key_params, kt, OPENVPN_OP_ENCRYPT, log_prefix);
key_ctx_update_implicit_iv(ctx, key_params);
ctx->epoch = key_params->epoch;
}

void
Expand All @@ -995,6 +1010,7 @@ init_key_bi_ctx_recv(struct key_ctx *ctx, const struct key_parameters *key_param
snprintf(log_prefix, sizeof(log_prefix), "Incoming %s", name);
init_key_ctx(ctx, key_params, kt, OPENVPN_OP_DECRYPT, log_prefix);
key_ctx_update_implicit_iv(ctx, key_params);
ctx->epoch = key_params->epoch;
}

void
Expand Down Expand Up @@ -1032,6 +1048,7 @@ free_key_ctx(struct key_ctx *ctx)
}
CLEAR(ctx->implicit_iv);
ctx->plaintext_blocks = 0;
ctx->epoch = 0;
}

void
Expand Down
52 changes: 50 additions & 2 deletions src/openvpn/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ struct key_parameters {

/** Number of bytes set in the HMac key material */
int hmac_size;

/** the epoch of the key. Only defined/non zero if key parameters
* represent a data channel epoch key parameters.
* Other uses of this struct leave this zero. */
uint16_t epoch;
};

/**
Expand All @@ -183,6 +188,11 @@ struct key_parameters {
void
key_parameters_from_key(struct key_parameters *key_params, const struct key *key);

struct epoch_key {
uint8_t epoch_key[SHA256_DIGEST_LENGTH];
uint16_t epoch;
};

/**
* Container for one set of cipher and/or HMAC contexts.
* @ingroup control_processor
Expand Down Expand Up @@ -211,6 +221,10 @@ struct key_ctx
uint64_t plaintext_blocks;
/** number of failed verification using this cipher */
uint64_t failed_verifications;
/** OpenVPN data channel epoch, this variable holds the
* epoch number this key belongs to. Note that epoch 0 is not used
* and epoch is always non-zero for epoch key contexts */
uint16_t epoch;
};

#define KEY_DIRECTION_BIDIRECTIONAL 0 /* same keys for both directions */
Expand Down Expand Up @@ -280,6 +294,39 @@ struct crypto_options
/**< OpenSSL cipher and HMAC contexts for
* both sending and receiving
* directions. */

/** last epoch_key used for generation of the current send data keys.
* As invariant, the epoch of epoch_key_send is always kept >= the epoch of
* epoch_key_recv */
struct epoch_key epoch_key_send;

/** epoch_key used for the highest receive epoch keys */
struct epoch_key epoch_key_recv;

/** the key_type that is used to generate the epoch keys */
struct key_type epoch_key_type;

/** The limit for AEAD cipher, this is the sum of packets + blocks
* that are allowed to be used. Will switch to a new epoch if this
* limit is reached*/
uint64_t aead_usage_limit;

/** Keeps the future epoch data keys for decryption. The current one
* that is expected to be used is stored in key_ctx_bi.
*
* for encryption keys this is not needed as we only need the current
* and move to another key by iteration and we never need to go back
* to an older key.
*/
struct key_ctx *epoch_data_keys_future;

/** number of keys stored in \c epoch_data_keys_future */
uint16_t epoch_data_keys_future_count;

/** The old key before the sender switched to a new epoch data key */
struct key_ctx epoch_retiring_data_receive_key;
struct packet_id_rec epoch_retiring_key_pid_recv;

struct packet_id packet_id; /**< Current packet ID state for both
* sending and receiving directions.
*
Expand All @@ -288,7 +335,7 @@ struct crypto_options
*
* The packet id also used as the IV
* for AEAD/OFB/CFG ciphers.
* */
*/
struct packet_id_persist *pid_persist;
/**< Persistent packet ID state for
* keeping state between successive
Expand Down Expand Up @@ -482,7 +529,8 @@ bool openvpn_decrypt(struct buffer *buf, struct buffer work,
* @return true if packet ID is validated to be not a replay, false otherwise.
*/
bool crypto_check_replay(struct crypto_options *opt,
const struct packet_id_net *pin, const char *error_prefix,
const struct packet_id_net *pin,
const char *error_prefix,
struct gc_arena *gc);


Expand Down
Loading

0 comments on commit 92adbc8

Please sign in to comment.