📄 crypto.c
字号:
/* * OpenVPN -- An application to securely tunnel IP networks * over a single UDP port, with support for TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002 James Yonan <jim@yonan.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 */#include "config.h"#ifdef USE_CRYPTO#include "syshead.h"#include "crypto.h"#include "error.h"#include "misc.h"#include "memdbg.h"/* * Check for key size creepage. */#if MAX_CIPHER_KEY_LENGTH < EVP_MAX_KEY_LENGTH#warning Some OpenSSL EVP ciphers now support key lengths greater than MAX_CIPHER_KEY_LENGTH -- consider increasing MAX_CIPHER_KEY_LENGTH#endif#if MAX_HMAC_KEY_LENGTH < EVP_MAX_MD_SIZE#warning Some OpenSSL HMAC message digests now support key lengths greater than MAX_HMAC_KEY_LENGTH -- consider increasing MAX_HMAC_KEY_LENGTH#endif/* * 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 EXTRA_FRAME bytes * of padding to leave room for downstream routines to prepend. * * Up to a total of EXTRA_FRAME 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 EXTRA_FRAME. This should not * happen unless the frame parameters are wrong. * * If opt->iv is not NULL it will be used and the residual * IV will be returned. * */#define CRYPT_ERROR(format) \ do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)#define CRYPT_ERROR_ARGS(format, args...) \ do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix, args); goto error_exit; } while (false)voidopenvpn_encrypt (struct buffer *buf, struct buffer work, const struct crypto_options *opt, const struct frame* frame, const time_t current){ 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 = opt->iv; uint8_t *residual_iv; const int iv_size = EVP_CIPHER_CTX_iv_length (ctx->cipher); const unsigned int mode = EVP_CIPHER_CTX_mode (ctx->cipher); int outlen; /* Put packet ID in plaintext buffer or IV, depending on cipher mode */ if (mode == EVP_CIPH_CBC_MODE) { if (opt->packet_id) { struct packet_id_net pin; packet_id_alloc_outgoing (&opt->packet_id->send, &pin, opt->packet_id_long_form); ASSERT (packet_id_write (&pin, buf, opt->packet_id_long_form, true)); } } else if (mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE) { struct packet_id_net pin; struct buffer b; ASSERT (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, 0, iv_size); buf_set_write (&b, iv, 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 EXTRA_FRAME bytes of prepend capacity */ ASSERT (buf_init (&work, EXTRA_FRAME (frame))); /* show the IV's initial state */ if (iv) msg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv, iv_size, 0)); msg (D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex (BPTR (buf), BLEN (buf), 80)); /* cipher_ctx was already initialized with key & keylen */ ASSERT (EVP_CipherInit_ov (ctx->cipher, NULL, NULL, iv, DO_ENCRYPT)); /* Buffer overflow check (should never happen) */ ASSERT (buf_safe (&work, buf->len + EVP_CIPHER_CTX_block_size (ctx->cipher))); /* Encrypt packet ID, payload */ ASSERT (EVP_CipherUpdate_ov (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))); work.len += outlen; /* Flush the encryption buffer */ ASSERT (EVP_CipherFinal (ctx->cipher, (residual_iv = BPTR (&work) + outlen), &outlen)); work.len += outlen; ASSERT (outlen == iv_size); /* prepend the IV to the ciphertext */ if (iv) { uint8_t *output = buf_prepend (&work, iv_size); ASSERT (output); memcpy (output, iv, iv_size); /* save the residual IV */ memcpy (iv, residual_iv, iv_size); } msg (D_PACKET_CONTENT, "ENCRYPT TO: %s", format_hex (BPTR (&work), BLEN (&work), 80)); } else /* No Encryption */ { if (opt->packet_id) { struct packet_id_net pin; packet_id_alloc_outgoing (&opt->packet_id->send, &pin, opt->packet_id_long_form); ASSERT (packet_id_write (&pin, buf, opt->packet_id_long_form, true)); } work = *buf; } /* HMAC the ciphertext (or plaintext if !cipher) */ if (ctx->hmac) { int hmac_len; uint8_t *output; HMAC_Init (ctx->hmac, NULL, 0, NULL); HMAC_Update (ctx->hmac, BPTR (&work), BLEN (&work)); output = buf_prepend (&work, HMAC_size (ctx->hmac)); ASSERT (output); HMAC_Final (ctx->hmac, output, &hmac_len); ASSERT (hmac_len == HMAC_size (ctx->hmac)); } *buf = work; } return;}/* * If opt->iv is not NULL, we will read an IV from the packet. * opt->iv is not modified. */voidopenvpn_decrypt (struct buffer *buf, struct buffer work, const struct crypto_options *opt, const struct frame* frame, const time_t current){ static const char error_prefix[] = "Authenticate/Decrypt packet error"; 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 */ int in_hmac_len; HMAC_Init (ctx->hmac, NULL, 0, NULL); /* Assume the length of the input HMAC */ hmac_len = HMAC_size (ctx->hmac); /* Authentication fails if insufficient data in packet for HMAC */ if (buf->len < hmac_len) CRYPT_ERROR ("missing authentication info"); HMAC_Update (ctx->hmac, BPTR (buf) + hmac_len, BLEN (buf) - hmac_len); HMAC_Final (ctx->hmac, local_hmac, &in_hmac_len); ASSERT (hmac_len == in_hmac_len); /* Compare locally computed HMAC with packet HMAC */ if (memcmp (local_hmac, BPTR (buf), hmac_len)) CRYPT_ERROR ("packet HMAC authentication failed"); ASSERT (buf_advance (buf, hmac_len)); } /* Decrypt packet ID + payload */ if (ctx->cipher) { const unsigned int mode = EVP_CIPHER_CTX_mode (ctx->cipher); const int iv_size = EVP_CIPHER_CTX_iv_length (ctx->cipher); uint8_t iv[EVP_MAX_IV_LENGTH]; int outlen; /* initialize work buffer with EXTRA_FRAME bytes of prepend capacity */ ASSERT (buf_init (&work, EXTRA_FRAME (frame))); /* use IV if user requested it */ CLEAR (iv); if (opt->iv) { if (buf->len < iv_size) CRYPT_ERROR ("missing IV info"); memcpy (iv, BPTR (buf), iv_size); ASSERT (buf_advance (buf, iv_size)); } /* show the IV's initial state */ if (iv) msg (D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex (iv, iv_size, 0)); if (buf->len < 1) CRYPT_ERROR ("missing payload"); /* ctx->cipher was already initialized with key & keylen */ if (!EVP_CipherInit_ov (ctx->cipher, NULL, NULL, iv, DO_DECRYPT)) 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 */ if (!EVP_CipherUpdate_ov (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))) CRYPT_ERROR ("cipher update failed"); work.len += outlen; /* Flush the decryption buffer */ if (!EVP_CipherFinal (ctx->cipher, BPTR (&work) + outlen, &outlen)) CRYPT_ERROR ("cipher final failed"); work.len += outlen; msg (D_PACKET_CONTENT, "DECRYPT TO: %s", format_hex (BPTR (&work), BLEN (&work), 80)); /* Get packet ID from plaintext buffer or IV, depending on cipher mode */ { if (mode == EVP_CIPH_CBC_MODE) { if (opt->packet_id) { if (!packet_id_read (&pin, &work, opt->packet_id_long_form)) CRYPT_ERROR ("error reading CBC packet-id"); have_pin = true; } } else if (mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE) { struct buffer b; ASSERT (iv); /* IV and packet-ID required */ ASSERT (opt->packet_id); /* for this mode. */ buf_set_read (&b, iv, 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, opt->packet_id_long_form)) CRYPT_ERROR ("error reading packet-id"); have_pin = true; } } if (have_pin) { if (packet_id_test (&opt->packet_id->rec, &pin)) packet_id_add (&opt->packet_id->rec, &pin); else CRYPT_ERROR_ARGS ("bad packet ID (may be a replay): %s", packet_id_net_print (&pin)); } *buf = work; } return; error_exit: buf->len = 0; return;}/* * How many bytes will we add to frame buffer for a given * set of crypto options? */voidcrypto_adjust_frame_parameters(struct frame *frame, const struct key_type* kt, bool cipher_defined, bool iv, bool packet_id, bool packet_id_long_form){ frame->extra_frame += (packet_id ? packet_id_size (packet_id_long_form) : 0) + ((cipher_defined && iv) ? EVP_CIPHER_iv_length (kt->cipher) : 0) + (cipher_defined ? EVP_CIPHER_block_size(kt->cipher) : 0) + /* worst case padding expansion */ kt->hmac_length;}static const EVP_CIPHER *get_cipher (const char *ciphername){ const EVP_CIPHER *cipher = NULL; ASSERT (ciphername); cipher = EVP_get_cipherbyname (ciphername); if ( !(cipher && cipher_ok (OBJ_nid2sn (EVP_CIPHER_nid (cipher)))))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -