📄 crypto.c
字号:
/* * 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. * * Copyright (C) 2002-2004 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 */#ifdef WIN32#include "config-win32.h"#else#include "config.h"#endif#ifdef USE_CRYPTO#include "syshead.h"#include "crypto.h"#include "error.h"#include "misc.h"#include "thread.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 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)voidopenvpn_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) { uint8_t iv_buf[EVP_MAX_IV_LENGTH]; const int iv_size = EVP_CIPHER_CTX_iv_length (ctx->cipher); const unsigned int mode = EVP_CIPHER_CTX_mode (ctx->cipher); int outlen; if (mode == EVP_CIPH_CBC_MODE) { CLEAR (iv_buf); /* generate pseudo-random IV */ if (opt->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, 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 (opt->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->use_iv) msg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc)); msg (D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); /* cipher_ctx was already initialized with key & keylen */ ASSERT (EVP_CipherInit_ov (ctx->cipher, NULL, NULL, iv_buf, 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, BPTR (&work) + outlen, &outlen)); work.len += outlen; ASSERT (outlen == iv_size); /* prepend the IV to the ciphertext */ if (opt->use_iv) { uint8_t *output = buf_prepend (&work, iv_size); ASSERT (output); memcpy (output, iv_buf, iv_size); } msg (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, 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_ex (ctx->hmac, NULL, 0, NULL, NULL); HMAC_Update (ctx->hmac, BPTR (&work), BLEN (&work)); output = buf_prepend (&work, HMAC_size (ctx->hmac)); ASSERT (output); HMAC_Final (ctx->hmac, output, (unsigned int *)&hmac_len); ASSERT (hmac_len == HMAC_size (ctx->hmac)); } *buf = work; } gc_free (&gc); return;}/* * If opt->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. */boolopenvpn_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 */ int in_hmac_len; HMAC_Init_ex (ctx->hmac, NULL, 0, NULL, 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, (unsigned int *)&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_buf[EVP_MAX_IV_LENGTH]; int outlen; /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); /* use IV if user requested it */ CLEAR (iv_buf); if (opt->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->use_iv) msg (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 */ if (!EVP_CipherInit_ov (ctx->cipher, NULL, NULL, iv_buf, 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, &gc)); /* 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 (opt->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, opt->packet_id_long_form)) CRYPT_ERROR ("error reading packet-id"); have_pin = !opt->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->packet_id_long_form) packet_id_persist_save_obj (opt->pid_persist, opt->packet_id); } else { msg (D_CRYPT_ERRORS, "%s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info", error_prefix, packet_id_net_print (&pin, true, &gc)); goto error_exit; } } *buf = work; } gc_free (&gc); return true; error_exit: buf->len = 0; gc_free (&gc); return false;}/* * 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 use_iv, bool packet_id, bool packet_id_long_form){ frame_add_to_extra_frame (frame, (packet_id ? packet_id_size (packet_id_long_form) : 0) + ((cipher_defined && use_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))))) msg (M_SSLERR, "Cipher algorithm '%s' not found", ciphername); if (EVP_CIPHER_key_length (cipher) > MAX_CIPHER_KEY_LENGTH) msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)", ciphername, EVP_CIPHER_key_length (cipher), MAX_CIPHER_KEY_LENGTH); return cipher;}static const EVP_MD *get_md (const char *digest){ const EVP_MD *md = NULL; ASSERT (digest); md = EVP_get_digestbyname (digest); if (!md) msg (M_SSLERR, "Message hash algorithm '%s' not found", digest); if (EVP_MD_size (md) > MAX_HMAC_KEY_LENGTH) msg (M_FATAL, "Message hash algorithm '%s' uses a default hash size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum hash size (%d bytes)", digest, EVP_MD_size (md), MAX_HMAC_KEY_LENGTH); return md;}static voidinit_cipher (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, struct key *key, const struct key_type *kt, int enc, const char *prefix){ struct gc_arena gc = gc_new (); EVP_CIPHER_CTX_init (ctx); if (!EVP_CipherInit_ov (ctx, cipher, NULL, NULL, enc)) msg (M_SSLERR, "EVP cipher init #1");#ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH if (!EVP_CIPHER_CTX_set_key_length (ctx, kt->cipher_length)) msg (M_SSLERR, "EVP set key size");#endif if (!EVP_CipherInit_ov (ctx, NULL, key->cipher, NULL, enc)) msg (M_SSLERR, "EVP cipher init #2"); msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key", prefix, OBJ_nid2sn (EVP_CIPHER_CTX_nid (ctx)), EVP_CIPHER_CTX_key_length (ctx) * 8); /* make sure we used a big enough key */ ASSERT (EVP_CIPHER_CTX_key_length (ctx) <= kt->cipher_length); msg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix, format_hex (key->cipher, kt->cipher_length, 0, &gc)); msg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d", prefix, EVP_CIPHER_CTX_block_size (ctx), EVP_CIPHER_CTX_iv_length (ctx)); gc_free (&gc);}static voidinit_hmac (HMAC_CTX *ctx, const EVP_MD *digest, struct key *key, const struct key_type *kt, const char *prefix){ struct gc_arena gc = gc_new (); HMAC_CTX_init (ctx); HMAC_Init_ex (ctx, key->hmac, kt->hmac_length, digest, NULL); msg (D_HANDSHAKE, "%s: Using %d bit message hash '%s' for HMAC authentication", prefix, HMAC_size (ctx) * 8, OBJ_nid2sn (EVP_MD_type (digest))); /* make sure we used a big enough key */ ASSERT (HMAC_size (ctx) <= kt->hmac_length); msg (D_SHOW_KEYS, "%s: HMAC KEY: %s", prefix, format_hex (key->hmac, kt->hmac_length, 0, &gc)); msg (D_CRYPTO_DEBUG, "%s: HMAC size=%d block_size=%d", prefix, EVP_MD_size (digest), EVP_MD_block_size (digest)); gc_free (&gc);}/* build a key_type */voidinit_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) { kt->cipher = get_cipher (ciphername); kt->cipher_length = EVP_CIPHER_key_length (kt->cipher); if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH) kt->cipher_length = keysize; /* check legal cipher mode */ { const unsigned int mode = EVP_CIPHER_mode (kt->cipher); if (!(mode == EVP_CIPH_CBC_MODE#ifdef ALLOW_NON_CBC_CIPHERS || (cfb_ofb_allowed && (mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE))#endif )) msg (M_FATAL, "Cipher '%s' uses a mode not supported by " PACKAGE_NAME " in your current configuration. CBC mode is always supported, while CFB and OFB modes are supported only when using SSL/TLS authentication and key exchange mode, and when " PACKAGE_NAME " has been built with ALLOW_NON_CBC_CIPHERS.", ciphername); } } else { if (warn) msg (M_WARN, "******* WARNING *******: null cipher specified, no encryption will be used"); } if (authname && authname_defined) { kt->digest = get_md (authname); kt->hmac_length = EVP_MD_size (kt->digest); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -