⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 crypto.c

📁 OpenVPN is a robust and highly flexible tunneling application that uses all of the encryption, authe
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  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 + -