From a8c6701c8b47429c32da5c01f4398f9e0b293c01 Mon Sep 17 00:00:00 2001
From: Kenny Root <kenny@the-b.org>
Date: Sun, 2 Jun 2013 20:52:01 -0700
Subject: [PATCH] Add AEAD cipher modes
Add Authenticated Encryption with Additional Data (AEAD) modes for
ciphers which obviates the need for a separate HMAC step. The MAC is
integrated into the cipher and the MAC tag is prepended to the payload
just as it is done with a separate MAC step.
---
configure.ac | 22 +++++++++++
src/openvpn/crypto.c | 92 +++++++++++++++++++++++++++++++++++++------
src/openvpn/crypto.h | 2 +
src/openvpn/crypto_backend.h | 31 +++++++++++++++
src/openvpn/crypto_openssl.c | 40 +++++++++++++++++++
src/openvpn/crypto_openssl.h | 13 ++++++
src/openvpn/crypto_polarssl.c | 15 +++++++
7 files changed, 203 insertions(+), 12 deletions(-)
diff --git a/configure.ac b/configure.ac
index 5da5772..53854d2 100644
a
|
b
|
AC_ARG_ENABLE( |
66 | 66 | ) |
67 | 67 | |
68 | 68 | AC_ARG_ENABLE( |
| 69 | [aead-modes], |
| 70 | [AS_HELP_STRING([--disable-aead-modes], [disable AEAD crypto modes @<:@default=yes@:>@])], |
| 71 | , |
| 72 | [enable_aead_modes="yes"] |
| 73 | ) |
| 74 | |
| 75 | AC_ARG_ENABLE( |
69 | 76 | [ssl], |
70 | 77 | [AS_HELP_STRING([--disable-ssl], [disable SSL support for TLS-based key exchange @<:@default=yes@:>@])], |
71 | 78 | , |
… |
… |
if test "${have_openssl_crypto}" = "yes"; then |
782 | 789 | [have_openssl_engine="no"; break] |
783 | 790 | ) |
784 | 791 | |
| 792 | have_openssl_aead_modes="yes" |
| 793 | AC_CHECK_FUNCS( |
| 794 | [ \ |
| 795 | EVP_aes_256_ccm \ |
| 796 | EVP_aes_256_gcm \ |
| 797 | EVP_aes_256_xts \ |
| 798 | ], |
| 799 | , |
| 800 | [have_openssl_aead_modes="no"; break] |
| 801 | ) |
| 802 | |
785 | 803 | CFLAGS="${saved_CFLAGS}" |
786 | 804 | LIBS="${saved_LIBS}" |
787 | 805 | fi |
… |
… |
case "${with_crypto_library}" in |
992 | 1010 | CRYPTO_SSL_LIBS="${OPENSSL_SSL_LIBS}" |
993 | 1011 | AC_DEFINE([ENABLE_CRYPTO_OPENSSL], [1], [Use OpenSSL library]) |
994 | 1012 | test "${have_openssl_engine}" = "yes" && AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [Use crypto library]) |
| 1013 | if test "${enable_aead_modes}" = "yes"; then |
| 1014 | test "${have_openssl_aead_modes}" = "yes" && AC_DEFINE([HAVE_AEAD_CIPHER_MODES], [1], [Use crypto library]) |
| 1015 | test "${have_openssl_aead_modes}" != "yes" && AC_MSG_ERROR([AEAD modes required but missing]) |
| 1016 | fi |
995 | 1017 | ;; |
996 | 1018 | polarssl) |
997 | 1019 | have_crypto_crypto="${have_polarssl_crypto}" |
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index d9adf5b..8c8cb57 100644
a
|
b
|
openvpn_encrypt (struct buffer *buf, struct buffer work, |
94 | 94 | if (buf->len > 0 && opt->key_ctx_bi) |
95 | 95 | { |
96 | 96 | struct key_ctx *ctx = &opt->key_ctx_bi->encrypt; |
| 97 | const unsigned int mode = cipher_ctx_mode (ctx->cipher); |
97 | 98 | |
98 | 99 | /* Do Encrypt from buf -> work */ |
99 | 100 | if (ctx->cipher) |
100 | 101 | { |
101 | 102 | uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH]; |
102 | 103 | const int iv_size = cipher_ctx_iv_length (ctx->cipher); |
103 | | const unsigned int mode = cipher_ctx_mode (ctx->cipher); |
104 | 104 | int outlen; |
105 | 105 | |
106 | 106 | if (mode == OPENVPN_MODE_CBC) |
… |
… |
openvpn_encrypt (struct buffer *buf, struct buffer work, |
132 | 132 | buf_set_write (&b, iv_buf, iv_size); |
133 | 133 | ASSERT (packet_id_write (&pin, &b, true, false)); |
134 | 134 | } |
135 | | else /* We only support CBC, CFB, or OFB modes right now */ |
| 135 | else if (aead_mode (mode)) |
| 136 | { |
| 137 | struct packet_id_net pin; |
| 138 | struct buffer b; |
| 139 | |
| 140 | ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */ |
| 141 | ASSERT (opt->packet_id); /* for this mode. */ |
| 142 | |
| 143 | packet_id_alloc_outgoing (&opt->packet_id->send, &pin, true); |
| 144 | prng_bytes (iv_buf, iv_size); |
| 145 | buf_set_write (&b, iv_buf, iv_size); |
| 146 | ASSERT (packet_id_write (&pin, &b, true, false)); |
| 147 | } |
| 148 | else /* We only support CBC, CFB, OFB, or AEAD modes right now */ |
136 | 149 | { |
137 | 150 | ASSERT (0); |
138 | 151 | } |
… |
… |
openvpn_encrypt (struct buffer *buf, struct buffer work, |
151 | 164 | ASSERT (cipher_ctx_reset(ctx->cipher, iv_buf)); |
152 | 165 | |
153 | 166 | /* Buffer overflow check */ |
154 | | if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher))) |
| 167 | int block_size = cipher_ctx_block_size(ctx->cipher); |
| 168 | if (!buf_safe (&work, buf->len + block_size)) |
155 | 169 | { |
156 | 170 | msg (D_CRYPT_ERRORS, "ENCRYPT: buffer size error, bc=%d bo=%d bl=%d wc=%d wo=%d wl=%d cbs=%d", |
157 | 171 | buf->capacity, |
… |
… |
openvpn_encrypt (struct buffer *buf, struct buffer work, |
160 | 174 | work.capacity, |
161 | 175 | work.offset, |
162 | 176 | work.len, |
163 | | cipher_ctx_block_size (ctx->cipher)); |
| 177 | block_size); |
164 | 178 | goto err; |
165 | 179 | } |
166 | 180 | |
| 181 | /* For AEAD ciphers, we need to update with the AD before ciphertext */ |
| 182 | if (aead_mode (mode)) |
| 183 | { |
| 184 | ASSERT (cipher_ctx_update_ad (ctx->cipher, iv_buf, iv_size)); |
| 185 | } |
| 186 | |
167 | 187 | /* Encrypt packet ID, payload */ |
168 | 188 | ASSERT (cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))); |
169 | 189 | work.len += outlen; |
… |
… |
openvpn_encrypt (struct buffer *buf, struct buffer work, |
171 | 191 | /* Flush the encryption buffer */ |
172 | 192 | ASSERT(cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen)); |
173 | 193 | work.len += outlen; |
174 | | ASSERT (outlen == iv_size); |
| 194 | |
| 195 | /* Some cipher modes don't need padding */ |
| 196 | if (block_size != 1) |
| 197 | ASSERT (outlen == iv_size); |
175 | 198 | |
176 | 199 | /* prepend the IV to the ciphertext */ |
177 | 200 | if (opt->flags & CO_USE_IV) |
… |
… |
openvpn_encrypt (struct buffer *buf, struct buffer work, |
206 | 229 | ASSERT (output); |
207 | 230 | hmac_ctx_final (ctx->hmac, output); |
208 | 231 | } |
| 232 | else if (aead_mode (mode)) |
| 233 | { |
| 234 | int tag_len = cipher_ctx_tag_length (ctx->cipher); |
| 235 | uint8_t* output = NULL; |
| 236 | |
| 237 | output = buf_prepend (&work, tag_len); |
| 238 | ASSERT (output); |
| 239 | ASSERT (cipher_ctx_get_tag (ctx->cipher, output, tag_len)); |
| 240 | } |
209 | 241 | |
210 | 242 | *buf = work; |
211 | 243 | } |
… |
… |
openvpn_decrypt (struct buffer *buf, struct buffer work, |
275 | 307 | const unsigned int mode = cipher_ctx_mode (ctx->cipher); |
276 | 308 | const int iv_size = cipher_ctx_iv_length (ctx->cipher); |
277 | 309 | uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH]; |
| 310 | int tag_size = cipher_ctx_tag_length (ctx->cipher); |
| 311 | uint8_t tag_buf[MAX_HMAC_KEY_LENGTH]; /* tag of AEAD ciphertext */ |
278 | 312 | int outlen; |
279 | 313 | |
280 | 314 | /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ |
281 | 315 | ASSERT (buf_init (&work, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_DECRYPT))); |
282 | 316 | |
| 317 | /* for AEAD ciphers, keep the tag value to feed in later */ |
| 318 | CLEAR (tag_buf); |
| 319 | if (aead_mode (mode)) |
| 320 | { |
| 321 | if (buf->len < tag_size) |
| 322 | CRYPT_ERROR ("missing tag"); |
| 323 | memcpy (tag_buf, BPTR (buf), tag_size); |
| 324 | ASSERT (buf_advance (buf, tag_size)); |
| 325 | } |
| 326 | |
283 | 327 | /* use IV if user requested it */ |
284 | 328 | CLEAR (iv_buf); |
285 | 329 | if (opt->flags & CO_USE_IV) |
… |
… |
openvpn_decrypt (struct buffer *buf, struct buffer work, |
305 | 349 | if (!buf_safe (&work, buf->len)) |
306 | 350 | CRYPT_ERROR ("buffer overflow"); |
307 | 351 | |
| 352 | /* feed in tag and the authenticated data for AEAD mode ciphers */ |
| 353 | if (aead_mode (mode)) |
| 354 | { |
| 355 | ASSERT (cipher_ctx_set_tag (ctx->cipher, tag_buf, tag_size)); |
| 356 | ASSERT (cipher_ctx_update_ad (ctx->cipher, iv_buf, iv_size)); |
| 357 | } |
| 358 | |
308 | 359 | /* Decrypt packet ID, payload */ |
309 | 360 | if (!cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))) |
310 | 361 | CRYPT_ERROR ("cipher update failed"); |
… |
… |
openvpn_decrypt (struct buffer *buf, struct buffer work, |
329 | 380 | have_pin = true; |
330 | 381 | } |
331 | 382 | } |
332 | | else if (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB) |
| 383 | else if (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB || aead_mode (mode)) |
333 | 384 | { |
334 | 385 | struct buffer b; |
335 | 386 | |
… |
… |
openvpn_decrypt (struct buffer *buf, struct buffer work, |
338 | 389 | |
339 | 390 | buf_set_read (&b, iv_buf, iv_size); |
340 | 391 | if (!packet_id_read (&pin, &b, true)) |
341 | | CRYPT_ERROR ("error reading CFB/OFB packet-id"); |
| 392 | CRYPT_ERROR ("error reading CFB/OFB/AEAD packet-id"); |
342 | 393 | have_pin = true; |
343 | 394 | } |
344 | | else /* We only support CBC, CFB, or OFB modes right now */ |
| 395 | else /* We only support CBC, CFB, OFB, or AEAD modes right now */ |
345 | 396 | { |
346 | 397 | ASSERT (0); |
347 | 398 | } |
… |
… |
init_key_type (struct key_type *kt, const char *ciphername, |
416 | 467 | bool authname_defined, int keysize, |
417 | 468 | bool cfb_ofb_allowed, bool warn) |
418 | 469 | { |
| 470 | bool aead_cipher = false; |
| 471 | |
419 | 472 | CLEAR (*kt); |
420 | 473 | if (ciphername && ciphername_defined) |
421 | 474 | { |
… |
… |
init_key_type (struct key_type *kt, const char *ciphername, |
427 | 480 | /* check legal cipher mode */ |
428 | 481 | { |
429 | 482 | const unsigned int mode = cipher_kt_mode (kt->cipher); |
430 | | if (!(mode == OPENVPN_MODE_CBC |
| 483 | aead_cipher = aead_mode (mode); |
| 484 | if (!(mode == OPENVPN_MODE_CBC || aead_cipher |
431 | 485 | #ifdef ALLOW_NON_CBC_CIPHERS |
432 | 486 | || (cfb_ofb_allowed && (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB)) |
433 | 487 | #endif |
… |
… |
init_key_type (struct key_type *kt, const char *ciphername, |
446 | 500 | } |
447 | 501 | if (authname && authname_defined) |
448 | 502 | { |
449 | | kt->digest = md_kt_get (authname); |
450 | | kt->hmac_length = md_kt_size (kt->digest); |
| 503 | if (aead_cipher) { |
| 504 | msg (M_FATAL, "AEAD mode cipher '%s' does not need an HMAC algorithm", ciphername); |
| 505 | } else { |
| 506 | kt->digest = md_kt_get (authname); |
| 507 | kt->hmac_length = md_kt_size (kt->digest); |
| 508 | } |
451 | 509 | } |
452 | | else |
| 510 | else if (!aead_cipher) |
453 | 511 | { |
454 | 512 | if (warn) |
455 | 513 | msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used"); |
… |
… |
cfb_ofb_mode (const struct key_type* kt) |
620 | 678 | return false; |
621 | 679 | } |
622 | 680 | |
| 681 | inline bool |
| 682 | aead_mode (int mode) |
| 683 | { |
| 684 | #ifdef HAVE_AEAD_CIPHER_MODES |
| 685 | return mode == OPENVPN_MODE_CCM || mode == OPENVPN_MODE_GCM || mode == OPENVPN_MODE_XTS; |
| 686 | #else |
| 687 | return false; |
| 688 | #endif |
| 689 | } |
| 690 | |
623 | 691 | /* |
624 | 692 | * Generate a random key. If key_type is provided, make |
625 | 693 | * sure generated key is valid for key_type. |
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 3b4b88e..76a1ba1 100644
a
|
b
|
int read_key (struct key *key, const struct key_type *kt, struct buffer *buf); |
191 | 191 | |
192 | 192 | bool cfb_ofb_mode (const struct key_type* kt); |
193 | 193 | |
| 194 | bool aead_mode (int mode); |
| 195 | |
194 | 196 | void init_key_type (struct key_type *kt, const char *ciphername, |
195 | 197 | bool ciphername_defined, const char *authname, bool authname_defined, |
196 | 198 | int keysize, bool cfb_ofb_allowed, bool warn); |
diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h
index 5ae47e6..4137b06 100644
a
|
b
|
void cipher_ctx_cleanup (cipher_ctx_t *ctx); |
269 | 269 | int cipher_ctx_iv_length (const cipher_ctx_t *ctx); |
270 | 270 | |
271 | 271 | /** |
| 272 | * Returns the MAC tag size of the cipher, in bytes. |
| 273 | * |
| 274 | * @param ctx The cipher's context |
| 275 | * |
| 276 | * @return Tag size, in bytes, or 0 if the cipher is not an |
| 277 | * authenticated encryption mode or ctx was NULL. |
| 278 | */ |
| 279 | int cipher_ctx_tag_length (const cipher_ctx_t *ctx); |
| 280 | |
| 281 | /** |
| 282 | * Sets the expected message authenticated code (MAC) tag for this cipher. |
| 283 | * |
| 284 | * @param ctx The cipher's context |
| 285 | * @param tag The expected MAC tag |
| 286 | * @param tag_size The tag's size, in bytes. |
| 287 | */ |
| 288 | int cipher_ctx_set_tag (cipher_ctx_t *ctx, uint8_t* tag, int tag_len); |
| 289 | |
| 290 | /** |
272 | 291 | * Returns the block size of the cipher, in bytes. |
273 | 292 | * |
274 | 293 | * @param ctx The cipher's context |
… |
… |
int cipher_ctx_mode (const cipher_ctx_t *ctx); |
299 | 318 | int cipher_ctx_reset (cipher_ctx_t *ctx, uint8_t *iv_buf); |
300 | 319 | |
301 | 320 | /** |
| 321 | * Updates the given cipher context, setting the additional data (AD) used |
| 322 | * with authenticated encryption with additional data (AEAD) cipher modes. |
| 323 | * |
| 324 | * @param ctx Cipher's context. May not be NULL. |
| 325 | * @param src Source buffer |
| 326 | * @param src_len Length of the source buffer, in bytes |
| 327 | * |
| 328 | * @return \c 0 on failure, \c 1 on success. |
| 329 | */ |
| 330 | int cipher_ctx_update_ad (cipher_ctx_t *ctx, uint8_t *src, int src_len); |
| 331 | |
| 332 | /** |
302 | 333 | * Updates the given cipher context, encrypting data in the source buffer, and |
303 | 334 | * placing any complete blocks in the destination buffer. |
304 | 335 | * |
diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c
index 1501bc8..5b214b4 100644
a
|
b
|
show_available_ciphers () |
316 | 316 | { |
317 | 317 | const unsigned int mode = EVP_CIPHER_mode (cipher); |
318 | 318 | if (mode == EVP_CIPH_CBC_MODE |
| 319 | #ifdef HAVE_AEAD_CIPHER_MODES |
| 320 | || mode == EVP_CIPH_CCM_MODE || mode == EVP_CIPH_GCM_MODE || mode == EVP_CIPH_XTS_MODE |
| 321 | #endif |
319 | 322 | #ifdef ALLOW_NON_CBC_CIPHERS |
320 | 323 | || mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE |
321 | 324 | #endif |
… |
… |
cipher_ctx_iv_length (const EVP_CIPHER_CTX *ctx) |
578 | 581 | } |
579 | 582 | |
580 | 583 | int |
| 584 | cipher_ctx_tag_length (const EVP_CIPHER_CTX *ctx) |
| 585 | { |
| 586 | /* add something better here? */ |
| 587 | return 16; |
| 588 | } |
| 589 | |
| 590 | int |
| 591 | cipher_ctx_set_tag (EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size) |
| 592 | { |
| 593 | #ifdef HAVE_AEAD_CIPHER_MODES |
| 594 | return EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_TAG, tag_size, tag_buf); |
| 595 | #else |
| 596 | ASSERT (0); |
| 597 | #endif |
| 598 | } |
| 599 | |
| 600 | int cipher_ctx_get_tag (EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size) |
| 601 | { |
| 602 | #ifdef HAVE_AEAD_CIPHER_MODES |
| 603 | return EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag_buf); |
| 604 | #else |
| 605 | ASSERT (0); |
| 606 | #endif |
| 607 | } |
| 608 | |
| 609 | int |
581 | 610 | cipher_ctx_block_size(const EVP_CIPHER_CTX *ctx) |
582 | 611 | { |
583 | 612 | return EVP_CIPHER_CTX_block_size (ctx); |
… |
… |
cipher_ctx_reset (EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) |
596 | 625 | } |
597 | 626 | |
598 | 627 | int |
| 628 | cipher_ctx_update_ad (EVP_CIPHER_CTX *ctx, uint8_t* src, int src_len) |
| 629 | { |
| 630 | #ifdef HAVE_AEAD_CIPHER_MODES |
| 631 | int len; |
| 632 | return EVP_CipherUpdate_ov (ctx, NULL, &len, src, src_len); |
| 633 | #else |
| 634 | ASSERT (0); |
| 635 | #endif |
| 636 | } |
| 637 | |
| 638 | int |
599 | 639 | cipher_ctx_update (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, |
600 | 640 | uint8_t *src, int src_len) |
601 | 641 | { |
diff --git a/src/openvpn/crypto_openssl.h b/src/openvpn/crypto_openssl.h
index f883c2a..7290818 100644
a
|
b
|
typedef HMAC_CTX hmac_ctx_t; |
61 | 61 | /** Cipher is in CFB mode */ |
62 | 62 | #define OPENVPN_MODE_CFB EVP_CIPH_CFB_MODE |
63 | 63 | |
| 64 | #ifdef HAVE_AEAD_CIPHER_MODES |
| 65 | |
| 66 | /** Cipher is in CCM mode */ |
| 67 | #define OPENVPN_MODE_CCM EVP_CIPH_CCM_MODE |
| 68 | |
| 69 | /** Cipher is in GCM mode */ |
| 70 | #define OPENVPN_MODE_GCM EVP_CIPH_GCM_MODE |
| 71 | |
| 72 | /** Cipher is in XTS mode */ |
| 73 | #define OPENVPN_MODE_XTS EVP_CIPH_XTS_MODE |
| 74 | |
| 75 | #endif /* HAVE_AEAD_CIPHER_MODES */ |
| 76 | |
64 | 77 | /** Cipher should encrypt */ |
65 | 78 | #define OPENVPN_OP_ENCRYPT 1 |
66 | 79 | |
diff --git a/src/openvpn/crypto_polarssl.c b/src/openvpn/crypto_polarssl.c
index 1f27d6c..91761d2 100644
a
|
b
|
int cipher_ctx_iv_length (const cipher_context_t *ctx) |
452 | 452 | return cipher_get_iv_size(ctx); |
453 | 453 | } |
454 | 454 | |
| 455 | int cipher_ctx_tag_length (const cipher_context_t *ctx) |
| 456 | { |
| 457 | ASSERT (0); |
| 458 | } |
| 459 | |
| 460 | int cipher_ctx_set_tag (cipher_ctx_t *ctx, uint8_t* tag, int tag_len) |
| 461 | { |
| 462 | ASSERT (0); |
| 463 | } |
| 464 | |
455 | 465 | int cipher_ctx_block_size(const cipher_context_t *ctx) |
456 | 466 | { |
457 | 467 | return cipher_get_block_size(ctx); |
… |
… |
int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf) |
469 | 479 | return 0 == cipher_reset(ctx, iv_buf); |
470 | 480 | } |
471 | 481 | |
| 482 | int cipher_ctx_update_ad (cipher_ctx_t *ctx, uint8_t *src, int src_len) |
| 483 | { |
| 484 | ASSERT (0); |
| 485 | } |
| 486 | |
472 | 487 | int cipher_ctx_update (cipher_context_t *ctx, uint8_t *dst, int *dst_len, |
473 | 488 | uint8_t *src, int src_len) |
474 | 489 | { |