2005-09-26 07:28:27 +02:00
/*
* 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 .
*
2010-04-28 18:31:36 +02:00
* Copyright ( C ) 2002 - 2010 OpenVPN Technologies , Inc . < sales @ openvpn . net >
2011-06-22 17:16:03 +02:00
* Copyright ( C ) 2010 Fox Crypto B . V . < openvpn @ fox - it . com >
2005-09-26 07:28:27 +02:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* 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
*/
2008-05-12 22:31:43 +02:00
# include "syshead.h"
2005-09-26 07:28:27 +02:00
# ifdef USE_CRYPTO
# include "crypto.h"
# include "error.h"
# include "misc.h"
# include "memdbg.h"
/*
* 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 )
void
openvpn_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 )
{
2011-06-23 09:05:12 +02:00
uint8_t iv_buf [ OPENVPN_MAX_IV_LENGTH ] ;
2005-09-26 07:28:27 +02:00
const int iv_size = EVP_CIPHER_CTX_iv_length ( ctx - > cipher ) ;
const unsigned int mode = EVP_CIPHER_CTX_mode ( ctx - > cipher ) ;
int outlen ;
2011-06-23 09:05:12 +02:00
if ( mode = = OPENVPN_MODE_CBC )
2005-09-26 07:28:27 +02:00
{
CLEAR ( iv_buf ) ;
/* generate pseudo-random IV */
if ( opt - > flags & CO_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 , BOOL_CAST ( opt - > flags & CO_PACKET_ID_LONG_FORM ) ) ;
ASSERT ( packet_id_write ( & pin , buf , BOOL_CAST ( opt - > flags & CO_PACKET_ID_LONG_FORM ) , true ) ) ;
}
}
2011-06-23 09:05:12 +02:00
else if ( mode = = OPENVPN_MODE_CFB | | mode = = OPENVPN_MODE_OFB )
2005-09-26 07:28:27 +02:00
{
struct packet_id_net pin ;
struct buffer b ;
ASSERT ( opt - > flags & CO_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 - > flags & CO_USE_IV )
dmsg ( D_PACKET_CONTENT , " ENCRYPT IV: %s " , format_hex ( iv_buf , iv_size , 0 , & gc ) ) ;
dmsg ( 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 */
if ( ! buf_safe ( & work , buf - > len + EVP_CIPHER_CTX_block_size ( ctx - > cipher ) ) )
{
msg ( D_CRYPT_ERRORS , " ENCRYPT: buffer size error, bc=%d bo=%d bl=%d wc=%d wo=%d wl=%d cbs=%d " ,
buf - > capacity ,
buf - > offset ,
buf - > len ,
work . capacity ,
work . offset ,
work . len ,
EVP_CIPHER_CTX_block_size ( ctx - > cipher ) ) ;
goto err ;
}
/* 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 - > flags & CO_USE_IV )
{
uint8_t * output = buf_prepend ( & work , iv_size ) ;
ASSERT ( output ) ;
memcpy ( output , iv_buf , iv_size ) ;
}
dmsg ( 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 , BOOL_CAST ( opt - > flags & CO_PACKET_ID_LONG_FORM ) ) ;
ASSERT ( packet_id_write ( & pin , buf , BOOL_CAST ( opt - > flags & CO_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 ;
err :
ERR_clear_error ( ) ;
buf - > len = 0 ;
gc_free ( & gc ) ;
return ;
}
/*
* If ( opt - > flags & CO_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 .
*/
bool
openvpn_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 ) ;
2011-06-23 09:05:12 +02:00
uint8_t iv_buf [ OPENVPN_MAX_IV_LENGTH ] ;
2005-09-26 07:28:27 +02:00
int outlen ;
/* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
ASSERT ( buf_init ( & work , FRAME_HEADROOM_ADJ ( frame , FRAME_HEADROOM_MARKER_DECRYPT ) ) ) ;
/* use IV if user requested it */
CLEAR ( iv_buf ) ;
if ( opt - > flags & CO_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 - > flags & CO_USE_IV )
dmsg ( 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 ;
dmsg ( 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 */
{
2011-06-23 09:05:12 +02:00
if ( mode = = OPENVPN_MODE_CBC )
2005-09-26 07:28:27 +02:00
{
if ( opt - > packet_id )
{
if ( ! packet_id_read ( & pin , & work , BOOL_CAST ( opt - > flags & CO_PACKET_ID_LONG_FORM ) ) )
CRYPT_ERROR ( " error reading CBC packet-id " ) ;
have_pin = true ;
}
}
2011-06-23 09:05:12 +02:00
else if ( mode = = OPENVPN_MODE_CFB | | mode = = OPENVPN_MODE_OFB )
2005-09-26 07:28:27 +02:00
{
struct buffer b ;
ASSERT ( opt - > flags & CO_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 , BOOL_CAST ( opt - > flags & CO_PACKET_ID_LONG_FORM ) ) )
CRYPT_ERROR ( " error reading packet-id " ) ;
have_pin = ! BOOL_CAST ( opt - > flags & CO_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 - > flags & CO_PACKET_ID_LONG_FORM ) )
packet_id_persist_save_obj ( opt - > pid_persist , opt - > packet_id ) ;
}
else
{
if ( ! ( opt - > flags & CO_MUTE_REPLAY_WARNINGS ) )
msg ( D_REPLAY_ERRORS , " %s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings " ,
error_prefix , packet_id_net_print ( & pin , true , & gc ) ) ;
goto error_exit ;
}
}
* buf = work ;
}
gc_free ( & gc ) ;
return true ;
error_exit :
ERR_clear_error ( ) ;
buf - > len = 0 ;
gc_free ( & gc ) ;
return false ;
}
/*
* How many bytes will we add to frame buffer for a given
* set of crypto options ?
*/
void
crypto_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 void
init_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 ) ;
dmsg ( D_SHOW_KEYS , " %s: CIPHER KEY: %s " , prefix ,
format_hex ( key - > cipher , kt - > cipher_length , 0 , & gc ) ) ;
dmsg ( 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 void
init_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 ) ;
dmsg ( D_SHOW_KEYS , " %s: HMAC KEY: %s " , prefix ,
format_hex ( key - > hmac , kt - > hmac_length , 0 , & gc ) ) ;
dmsg ( 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 struct key_type .
*/
void
init_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 ) ;
2011-06-23 09:05:12 +02:00
if ( ! ( mode = = OPENVPN_MODE_CBC
2005-09-26 07:28:27 +02:00
# ifdef ALLOW_NON_CBC_CIPHERS
2011-06-23 09:05:12 +02:00
| | ( cfb_ofb_allowed & & ( mode = = OPENVPN_MODE_CFB | | mode = = OPENVPN_MODE_OFB ) )
2005-09-26 07:28:27 +02:00
# endif
) )
# ifdef ENABLE_SMALL
msg ( M_FATAL , " Cipher '%s' mode not supported " , ciphername ) ;
# else
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 ) ;
# endif
}
}
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
{
if ( warn )
msg ( M_WARN , " ******* WARNING *******: null MAC specified, no authentication will be used " ) ;
}
}
const char *
kt_cipher_name ( const struct key_type * kt )
{
if ( kt - > cipher )
return EVP_CIPHER_name ( kt - > cipher ) ;
else
return " [null-cipher] " ;
}
const char *
kt_digest_name ( const struct key_type * kt )
{
if ( kt - > digest )
return EVP_MD_name ( kt - > digest ) ;
else
return " [null-digest] " ;
}
int
kt_key_size ( const struct key_type * kt )
{
if ( kt - > cipher_length )
return kt - > cipher_length * 8 ;
else if ( kt - > cipher )
return EVP_CIPHER_key_length ( kt - > cipher ) * 8 ;
else
return 0 ;
}
/* given a key and key_type, build a key_ctx */
void
init_key_ctx ( struct key_ctx * ctx , struct key * key ,
const struct key_type * kt , int enc ,
const char * prefix )
{
CLEAR ( * ctx ) ;
if ( kt - > cipher & & kt - > cipher_length > 0 )
{
ALLOC_OBJ ( ctx - > cipher , EVP_CIPHER_CTX ) ;
init_cipher ( ctx - > cipher , kt - > cipher , key , kt , enc , prefix ) ;
}
if ( kt - > digest & & kt - > hmac_length > 0 )
{
ALLOC_OBJ ( ctx - > hmac , HMAC_CTX ) ;
init_hmac ( ctx - > hmac , kt - > digest , key , kt , prefix ) ;
}
}
void
free_key_ctx ( struct key_ctx * ctx )
{
if ( ctx - > cipher )
{
EVP_CIPHER_CTX_cleanup ( ctx - > cipher ) ;
free ( ctx - > cipher ) ;
ctx - > cipher = NULL ;
}
if ( ctx - > hmac )
{
HMAC_CTX_cleanup ( ctx - > hmac ) ;
free ( ctx - > hmac ) ;
ctx - > hmac = NULL ;
}
}
void
free_key_ctx_bi ( struct key_ctx_bi * ctx )
{
free_key_ctx ( & ctx - > encrypt ) ;
free_key_ctx ( & ctx - > decrypt ) ;
}
/*
* Return number of DES cblocks for the current
* key type or 0 if not a DES cipher .
*/
static int
n_DES_cblocks ( const struct key_type * kt )
{
int ret = 0 ;
const char * name = OBJ_nid2sn ( EVP_CIPHER_nid ( kt - > cipher ) ) ;
if ( name )
{
if ( ! strncmp ( name , " DES- " , 4 ) )
{
ret = EVP_CIPHER_key_length ( kt - > cipher ) / sizeof ( DES_cblock ) ;
}
else if ( ! strncmp ( name , " DESX- " , 5 ) )
{
ret = 1 ;
}
}
dmsg ( D_CRYPTO_DEBUG , " CRYPTO INFO: n_DES_cblocks=%d " , ret ) ;
return ret ;
}
static bool
check_key_DES ( struct key * key , const struct key_type * kt , int ndc )
{
int i ;
struct buffer b ;
buf_set_read ( & b , key - > cipher , kt - > cipher_length ) ;
for ( i = 0 ; i < ndc ; + + i )
{
DES_cblock * dc = ( DES_cblock * ) buf_read_alloc ( & b , sizeof ( DES_cblock ) ) ;
if ( ! dc )
{
msg ( D_CRYPT_ERRORS , " CRYPTO INFO: check_key_DES: insufficient key material " ) ;
goto err ;
}
if ( DES_is_weak_key ( dc ) )
{
msg ( D_CRYPT_ERRORS , " CRYPTO INFO: check_key_DES: weak key detected " ) ;
goto err ;
}
if ( ! DES_check_key_parity ( dc ) )
{
msg ( D_CRYPT_ERRORS , " CRYPTO INFO: check_key_DES: bad parity detected " ) ;
goto err ;
}
}
return true ;
err :
ERR_clear_error ( ) ;
return false ;
}
static void
fixup_key_DES ( struct key * key , const struct key_type * kt , int ndc )
{
int i ;
struct buffer b ;
buf_set_read ( & b , key - > cipher , kt - > cipher_length ) ;
for ( i = 0 ; i < ndc ; + + i )
{
DES_cblock * dc = ( DES_cblock * ) buf_read_alloc ( & b , sizeof ( DES_cblock ) ) ;
if ( ! dc )
{
msg ( D_CRYPT_ERRORS , " CRYPTO INFO: fixup_key_DES: insufficient key material " ) ;
ERR_clear_error ( ) ;
return ;
}
DES_set_odd_parity ( dc ) ;
}
}
static bool
key_is_zero ( struct key * key , const struct key_type * kt )
{
int i ;
for ( i = 0 ; i < kt - > cipher_length ; + + i )
if ( key - > cipher [ i ] )
return false ;
msg ( D_CRYPT_ERRORS , " CRYPTO INFO: WARNING: zero key detected " ) ;
return true ;
}
/*
* Make sure that cipher key is a valid key for current key_type .
*/
bool
check_key ( struct key * key , const struct key_type * kt )
{
if ( kt - > cipher )
{
/*
* Check for zero key
*/
if ( key_is_zero ( key , kt ) )
return false ;
/*
* Check for weak or semi - weak DES keys .
*/
{
const int ndc = n_DES_cblocks ( kt ) ;
if ( ndc )
return check_key_DES ( key , kt , ndc ) ;
else
return true ;
}
}
return true ;
}
/*
* Make safe mutations to key to ensure it is valid ,
* such as ensuring correct parity on DES keys .
*
* This routine cannot guarantee it will generate a good
* key . You must always call check_key after this routine
* to make sure .
*/
void
fixup_key ( struct key * key , const struct key_type * kt )
{
struct gc_arena gc = gc_new ( ) ;
if ( kt - > cipher )
{
# ifdef ENABLE_DEBUG
const struct key orig = * key ;
# endif
const int ndc = n_DES_cblocks ( kt ) ;
if ( ndc )
fixup_key_DES ( key , kt , ndc ) ;
# ifdef ENABLE_DEBUG
if ( check_debug_level ( D_CRYPTO_DEBUG ) )
{
if ( memcmp ( orig . cipher , key - > cipher , kt - > cipher_length ) )
dmsg ( D_CRYPTO_DEBUG , " CRYPTO INFO: fixup_key: before=%s after=%s " ,
format_hex ( orig . cipher , kt - > cipher_length , 0 , & gc ) ,
format_hex ( key - > cipher , kt - > cipher_length , 0 , & gc ) ) ;
}
# endif
}
gc_free ( & gc ) ;
}
void
check_replay_iv_consistency ( const struct key_type * kt , bool packet_id , bool use_iv )
{
if ( cfb_ofb_mode ( kt ) & & ! ( packet_id & & use_iv ) )
msg ( M_FATAL , " --no-replay or --no-iv cannot be used with a CFB or OFB mode cipher " ) ;
}
bool
cfb_ofb_mode ( const struct key_type * kt )
{
const unsigned int mode = EVP_CIPHER_mode ( kt - > cipher ) ;
2011-06-23 09:05:12 +02:00
if ( kt & & kt - > cipher ) {
return mode = = OPENVPN_MODE_CFB | | mode = = OPENVPN_MODE_OFB ;
}
return false ;
2005-09-26 07:28:27 +02:00
}
/*
* Generate a random key . If key_type is provided , make
* sure generated key is valid for key_type .
*/
void
generate_key_random ( struct key * key , const struct key_type * kt )
{
int cipher_len = MAX_CIPHER_KEY_LENGTH ;
int hmac_len = MAX_HMAC_KEY_LENGTH ;
struct gc_arena gc = gc_new ( ) ;
do {
CLEAR ( * key ) ;
if ( kt )
{
if ( kt - > cipher & & kt - > cipher_length > 0 & & kt - > cipher_length < = cipher_len )
cipher_len = kt - > cipher_length ;
if ( kt - > digest & & kt - > hmac_length > 0 & & kt - > hmac_length < = hmac_len )
hmac_len = kt - > hmac_length ;
}
2011-06-22 17:16:03 +02:00
if ( ! rand_bytes ( key - > cipher , cipher_len )
| | ! rand_bytes ( key - > hmac , hmac_len ) )
2005-09-26 07:28:27 +02:00
msg ( M_FATAL , " ERROR: Random number generator cannot obtain entropy for key generation " ) ;
dmsg ( D_SHOW_KEY_SOURCE , " Cipher source entropy: %s " , format_hex ( key - > cipher , cipher_len , 0 , & gc ) ) ;
dmsg ( D_SHOW_KEY_SOURCE , " HMAC source entropy: %s " , format_hex ( key - > hmac , hmac_len , 0 , & gc ) ) ;
if ( kt )
fixup_key ( key , kt ) ;
} while ( kt & & ! check_key ( key , kt ) ) ;
gc_free ( & gc ) ;
}
/*
* Print key material
*/
void
key2_print ( const struct key2 * k ,
const struct key_type * kt ,
const char * prefix0 ,
const char * prefix1 )
{
struct gc_arena gc = gc_new ( ) ;
ASSERT ( k - > n = = 2 ) ;
dmsg ( D_SHOW_KEY_SOURCE , " %s (cipher): %s " ,
prefix0 ,
format_hex ( k - > keys [ 0 ] . cipher , kt - > cipher_length , 0 , & gc ) ) ;
dmsg ( D_SHOW_KEY_SOURCE , " %s (hmac): %s " ,
prefix0 ,
format_hex ( k - > keys [ 0 ] . hmac , kt - > hmac_length , 0 , & gc ) ) ;
dmsg ( D_SHOW_KEY_SOURCE , " %s (cipher): %s " ,
prefix1 ,
format_hex ( k - > keys [ 1 ] . cipher , kt - > cipher_length , 0 , & gc ) ) ;
dmsg ( D_SHOW_KEY_SOURCE , " %s (hmac): %s " ,
prefix1 ,
format_hex ( k - > keys [ 1 ] . hmac , kt - > hmac_length , 0 , & gc ) ) ;
gc_free ( & gc ) ;
}
void
test_crypto ( const struct crypto_options * co , struct frame * frame )
{
int i , j ;
struct gc_arena gc = gc_new ( ) ;
struct buffer src = alloc_buf_gc ( TUN_MTU_SIZE ( frame ) , & gc ) ;
struct buffer work = alloc_buf_gc ( BUF_SIZE ( frame ) , & gc ) ;
struct buffer encrypt_workspace = alloc_buf_gc ( BUF_SIZE ( frame ) , & gc ) ;
struct buffer decrypt_workspace = alloc_buf_gc ( BUF_SIZE ( frame ) , & gc ) ;
struct buffer buf = clear_buf ( ) ;
/* init work */
ASSERT ( buf_init ( & work , FRAME_HEADROOM ( frame ) ) ) ;
msg ( M_INFO , " Entering " PACKAGE_NAME " crypto self-test mode. " ) ;
for ( i = 1 ; i < = TUN_MTU_SIZE ( frame ) ; + + i )
{
update_time ( ) ;
msg ( M_INFO , " TESTING ENCRYPT/DECRYPT of packet length=%d " , i ) ;
/*
* Load src with random data .
*/
ASSERT ( buf_init ( & src , 0 ) ) ;
ASSERT ( i < = src . capacity ) ;
src . len = i ;
2011-06-22 17:16:03 +02:00
ASSERT ( rand_bytes ( BPTR ( & src ) , BLEN ( & src ) ) ) ;
2005-09-26 07:28:27 +02:00
/* copy source to input buf */
buf = work ;
memcpy ( buf_write_alloc ( & buf , BLEN ( & src ) ) , BPTR ( & src ) , BLEN ( & src ) ) ;
/* encrypt */
openvpn_encrypt ( & buf , encrypt_workspace , co , frame ) ;
/* decrypt */
openvpn_decrypt ( & buf , decrypt_workspace , co , frame ) ;
/* compare */
if ( buf . len ! = src . len )
msg ( M_FATAL , " SELF TEST FAILED, src.len=%d buf.len=%d " , src . len , buf . len ) ;
for ( j = 0 ; j < i ; + + j )
{
const uint8_t in = * ( BPTR ( & src ) + j ) ;
const uint8_t out = * ( BPTR ( & buf ) + j ) ;
if ( in ! = out )
msg ( M_FATAL , " SELF TEST FAILED, pos=%d in=%d out=%d " , j , in , out ) ;
}
}
msg ( M_INFO , PACKAGE_NAME " crypto self-test mode SUCCEEDED. " ) ;
gc_free ( & gc ) ;
}
# ifdef USE_SSL
2005-12-08 19:29:38 +01:00
2005-09-26 07:28:27 +02:00
void
get_tls_handshake_key ( const struct key_type * key_type ,
struct key_ctx_bi * ctx ,
const char * passphrase_file ,
2005-12-29 08:47:47 +01:00
const int key_direction ,
2005-12-08 19:29:38 +01:00
const unsigned int flags )
2005-09-26 07:28:27 +02:00
{
if ( passphrase_file & & key_type - > hmac_length )
{
struct key2 key2 ;
struct key_type kt = * key_type ;
struct key_direction_state kds ;
/* for control channel we are only authenticating, not encrypting */
kt . cipher_length = 0 ;
kt . cipher = NULL ;
2005-12-08 19:29:38 +01:00
# if ENABLE_INLINE_FILES
if ( flags & GHK_INLINE )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
/* key was specified inline, key text is in passphrase_file */
read_key_file ( & key2 , passphrase_file , RKF_INLINE | RKF_MUST_SUCCEED ) ;
/* succeeded? */
if ( key2 . n = = 2 )
msg ( M_INFO , " Control Channel Authentication: tls-auth using INLINE static key file " ) ;
else
msg ( M_FATAL , " INLINE tls-auth file lacks the requisite 2 keys " ) ;
2005-09-26 07:28:27 +02:00
}
else
2005-12-08 19:29:38 +01:00
# endif
{
/* first try to parse as an OpenVPN static key file */
read_key_file ( & key2 , passphrase_file , 0 ) ;
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
/* succeeded? */
if ( key2 . n = = 2 )
{
msg ( M_INFO ,
" Control Channel Authentication: using '%s' as a " PACKAGE_NAME " static key file " ,
passphrase_file ) ;
}
else
{
int hash_size ;
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
CLEAR ( key2 ) ;
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
/* failed, now try to get hash from a freeform file */
hash_size = read_passphrase_hash ( passphrase_file ,
kt . digest ,
key2 . keys [ 0 ] . hmac ,
MAX_HMAC_KEY_LENGTH ) ;
ASSERT ( hash_size = = kt . hmac_length ) ;
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
/* suceeded */
key2 . n = 1 ;
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
msg ( M_INFO ,
" Control Channel Authentication: using '%s' as a free-form passphrase file " ,
passphrase_file ) ;
}
}
2005-09-26 07:28:27 +02:00
/* handle key direction */
2005-12-29 08:47:47 +01:00
key_direction_state_init ( & kds , key_direction ) ;
2005-09-26 07:28:27 +02:00
must_have_n_keys ( passphrase_file , " tls-auth " , & key2 , kds . need_keys ) ;
/* initialize hmac key in both directions */
2011-06-23 09:05:12 +02:00
init_key_ctx ( & ctx - > encrypt , & key2 . keys [ kds . out_key ] , & kt , OPENVPN_OP_ENCRYPT ,
2005-09-26 07:28:27 +02:00
" Outgoing Control Channel Authentication " ) ;
2011-06-23 09:05:12 +02:00
init_key_ctx ( & ctx - > decrypt , & key2 . keys [ kds . in_key ] , & kt , OPENVPN_OP_DECRYPT ,
2005-09-26 07:28:27 +02:00
" Incoming Control Channel Authentication " ) ;
CLEAR ( key2 ) ;
}
else
{
CLEAR ( * ctx ) ;
}
}
# endif
/* header and footer for static key file */
2010-03-31 08:38:21 +02:00
static const char static_key_head [ ] = " -----BEGIN OpenVPN Static key V1----- " ;
static const char static_key_foot [ ] = " -----END OpenVPN Static key V1----- " ;
2005-09-26 07:28:27 +02:00
static const char printable_char_fmt [ ] =
" Non-Hex character ('%c') found at line %d in key file '%s' (%d/%d/%d bytes found/min/max) " ;
static const char unprintable_char_fmt [ ] =
" Non-Hex, unprintable character (0x%02x) found at line %d in key file '%s' (%d/%d/%d bytes found/min/max) " ;
/* read key from file */
2005-12-08 19:29:38 +01:00
2005-09-26 07:28:27 +02:00
void
2005-12-08 19:29:38 +01:00
read_key_file ( struct key2 * key2 , const char * file , const unsigned int flags )
2005-09-26 07:28:27 +02:00
{
struct gc_arena gc = gc_new ( ) ;
2005-12-08 19:29:38 +01:00
struct buffer in ;
2005-09-26 07:28:27 +02:00
int fd , size ;
uint8_t hex_byte [ 3 ] = { 0 , 0 , 0 } ;
2005-12-08 19:29:38 +01:00
const char * error_filename = file ;
2005-09-26 07:28:27 +02:00
/* parse info */
2006-06-24 10:04:42 +02:00
const unsigned char * cp ;
2005-09-26 07:28:27 +02:00
int hb_index = 0 ;
int line_num = 1 ;
int line_index = 0 ;
int match = 0 ;
/* output */
uint8_t * out = ( uint8_t * ) & key2 - > keys ;
const int keylen = sizeof ( key2 - > keys ) ;
int count = 0 ;
/* parse states */
# define PARSE_INITIAL 0
# define PARSE_HEAD 1
# define PARSE_DATA 2
# define PARSE_DATA_COMPLETE 3
# define PARSE_FOOT 4
# define PARSE_FINISHED 5
int state = PARSE_INITIAL ;
/* constants */
const int hlen = strlen ( static_key_head ) ;
const int flen = strlen ( static_key_foot ) ;
const int onekeylen = sizeof ( key2 - > keys [ 0 ] ) ;
CLEAR ( * key2 ) ;
2005-12-08 19:29:38 +01:00
/*
* Key can be provided as a filename in ' file ' or if RKF_INLINE
* is set , the actual key data itself in ascii form .
*/
# if ENABLE_INLINE_FILES
if ( flags & RKF_INLINE ) /* 'file' is a string containing ascii representation of key */
{
size = strlen ( file ) + 1 ;
buf_set_read ( & in , ( const uint8_t * ) file , size ) ;
error_filename = INLINE_FILE_TAG ;
}
else /* 'file' is a filename which refers to a file containing the ascii key */
# endif
{
in = alloc_buf_gc ( 2048 , & gc ) ;
fd = open ( file , O_RDONLY ) ;
if ( fd = = - 1 )
msg ( M_ERR , " Cannot open file key file '%s' " , file ) ;
size = read ( fd , in . data , in . capacity ) ;
2008-07-17 06:25:50 +02:00
if ( size < 0 )
msg ( M_FATAL , " Read error on key file ('%s') " , file ) ;
2005-12-08 19:29:38 +01:00
if ( size = = in . capacity )
msg ( M_FATAL , " Key file ('%s') can be a maximum of %d bytes " , file , ( int ) in . capacity ) ;
close ( fd ) ;
}
2005-09-26 07:28:27 +02:00
2006-06-24 10:04:42 +02:00
cp = ( unsigned char * ) in . data ;
2008-07-17 06:25:50 +02:00
while ( size > 0 )
2005-09-26 07:28:27 +02:00
{
2006-06-24 10:04:42 +02:00
const unsigned char c = * cp ;
2005-09-26 07:28:27 +02:00
#if 0
2010-03-31 08:38:21 +02:00
msg ( M_INFO , " char='%c'[%d] s=%d ln=%d li=%d m=%d c=%d " ,
c , ( int ) c , state , line_num , line_index , match , count ) ;
2005-09-26 07:28:27 +02:00
# endif
2005-12-08 19:29:38 +01:00
if ( c = = ' \n ' )
{
line_index = match = 0 ;
+ + line_num ;
}
else
{
/* first char of new line */
if ( ! line_index )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
/* first char of line after header line? */
if ( state = = PARSE_HEAD )
state = PARSE_DATA ;
/* first char of footer */
if ( ( state = = PARSE_DATA | | state = = PARSE_DATA_COMPLETE ) & & c = = ' - ' )
state = PARSE_FOOT ;
2005-09-26 07:28:27 +02:00
}
2005-12-08 19:29:38 +01:00
/* compare read chars with header line */
if ( state = = PARSE_INITIAL )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
if ( line_index < hlen & & c = = static_key_head [ line_index ] )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
if ( + + match = = hlen )
state = PARSE_HEAD ;
2005-09-26 07:28:27 +02:00
}
2005-12-08 19:29:38 +01:00
}
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
/* compare read chars with footer line */
if ( state = = PARSE_FOOT )
{
if ( line_index < flen & & c = = static_key_foot [ line_index ] )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
if ( + + match = = flen )
state = PARSE_FINISHED ;
2005-09-26 07:28:27 +02:00
}
2005-12-08 19:29:38 +01:00
}
2005-09-26 07:28:27 +02:00
2005-12-08 19:29:38 +01:00
/* reading key */
if ( state = = PARSE_DATA )
{
if ( isxdigit ( c ) )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
ASSERT ( hb_index > = 0 & & hb_index < 2 ) ;
hex_byte [ hb_index + + ] = c ;
if ( hb_index = = 2 )
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
unsigned int u ;
ASSERT ( sscanf ( ( const char * ) hex_byte , " %x " , & u ) = = 1 ) ;
* out + + = u ;
hb_index = 0 ;
if ( + + count = = keylen )
state = PARSE_DATA_COMPLETE ;
2005-09-26 07:28:27 +02:00
}
}
2005-12-08 19:29:38 +01:00
else if ( isspace ( c ) )
;
else
2005-09-26 07:28:27 +02:00
{
2005-12-08 19:29:38 +01:00
msg ( M_FATAL ,
( isprint ( c ) ? printable_char_fmt : unprintable_char_fmt ) ,
c , line_num , error_filename , count , onekeylen , keylen ) ;
2005-09-26 07:28:27 +02:00
}
}
2005-12-08 19:29:38 +01:00
+ + line_index ;
2005-09-26 07:28:27 +02:00
}
2005-12-08 19:29:38 +01:00
+ + cp ;
- - size ;
2005-09-26 07:28:27 +02:00
}
/*
* Normally we will read either 1 or 2 keys from file .
*/
key2 - > n = count / onekeylen ;
ASSERT ( key2 - > n > = 0 & & key2 - > n < = ( int ) SIZE ( key2 - > keys ) ) ;
2005-12-08 19:29:38 +01:00
if ( flags & RKF_MUST_SUCCEED )
2005-09-26 07:28:27 +02:00
{
if ( ! key2 - > n )
2010-03-31 08:38:21 +02:00
msg ( M_FATAL , " Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max) " ,
2005-12-08 19:29:38 +01:00
error_filename , count , onekeylen , keylen ) ;
2005-09-26 07:28:27 +02:00
if ( state ! = PARSE_FINISHED )
msg ( M_FATAL , " Footer text not found in file '%s' (%d/%d/%d bytes found/min/max) " ,
2005-12-08 19:29:38 +01:00
error_filename , count , onekeylen , keylen ) ;
2005-09-26 07:28:27 +02:00
}
2008-12-25 03:33:34 +01:00
/* zero file read buffer if not an inline file */
# if ENABLE_INLINE_FILES
if ( ! ( flags & RKF_INLINE ) )
# endif
buf_clear ( & in ) ;
2005-09-26 07:28:27 +02:00
if ( key2 - > n )
2005-12-08 19:29:38 +01:00
warn_if_group_others_accessible ( error_filename ) ;
2005-09-26 07:28:27 +02:00
#if 0
/* DEBUGGING */
{
int i ;
printf ( " KEY READ, n=%d \n " , key2 - > n ) ;
for ( i = 0 ; i < ( int ) SIZE ( key2 - > keys ) ; + + i )
{
/* format key as ascii */
const char * fmt = format_hex_ex ( ( const uint8_t * ) & key2 - > keys [ i ] ,
sizeof ( key2 - > keys [ i ] ) ,
0 ,
16 ,
" \n " ,
& gc ) ;
printf ( " [%d] \n %s \n \n " , i , fmt ) ;
}
}
# endif
/* pop our garbage collection level */
gc_free ( & gc ) ;
}
int
read_passphrase_hash ( const char * passphrase_file ,
const EVP_MD * digest ,
uint8_t * output ,
int len )
{
unsigned int outlen = 0 ;
EVP_MD_CTX md ;
ASSERT ( len > = EVP_MD_size ( digest ) ) ;
memset ( output , 0 , len ) ;
EVP_DigestInit ( & md , digest ) ;
/* read passphrase file */
{
const int min_passphrase_size = 8 ;
uint8_t buf [ 64 ] ;
int total_size = 0 ;
int fd = open ( passphrase_file , O_RDONLY ) ;
if ( fd = = - 1 )
msg ( M_ERR , " Cannot open passphrase file: '%s' " , passphrase_file ) ;
for ( ; ; )
{
int size = read ( fd , buf , sizeof ( buf ) ) ;
if ( size = = 0 )
break ;
if ( size = = - 1 )
msg ( M_ERR , " Read error on passphrase file: '%s' " ,
passphrase_file ) ;
EVP_DigestUpdate ( & md , buf , size ) ;
total_size + = size ;
}
close ( fd ) ;
warn_if_group_others_accessible ( passphrase_file ) ;
if ( total_size < min_passphrase_size )
msg ( M_FATAL ,
" Passphrase file '%s' is too small (must have at least %d characters) " ,
passphrase_file , min_passphrase_size ) ;
}
EVP_DigestFinal ( & md , output , & outlen ) ;
EVP_MD_CTX_cleanup ( & md ) ;
return outlen ;
}
/*
* Write key to file , return number of random bits
* written .
*/
int
write_key_file ( const int nkeys , const char * filename )
{
struct gc_arena gc = gc_new ( ) ;
int fd , i ;
int nbits = 0 ;
/* must be large enough to hold full key file */
struct buffer out = alloc_buf_gc ( 2048 , & gc ) ;
struct buffer nbits_head_text = alloc_buf_gc ( 128 , & gc ) ;
/* how to format the ascii file representation of key */
const int bytes_per_line = 16 ;
/* open key file */
fd = open ( filename , O_CREAT | O_TRUNC | O_WRONLY , S_IRUSR | S_IWUSR ) ;
if ( fd = = - 1 )
msg ( M_ERR , " Cannot open shared secret file '%s' for write " , filename ) ;
buf_printf ( & out , " %s \n " , static_key_head ) ;
for ( i = 0 ; i < nkeys ; + + i )
{
struct key key ;
char * fmt ;
/* generate random bits */
generate_key_random ( & key , NULL ) ;
/* format key as ascii */
fmt = format_hex_ex ( ( const uint8_t * ) & key ,
sizeof ( key ) ,
0 ,
bytes_per_line ,
" \n " ,
& gc ) ;
/* increment random bits counter */
nbits + = sizeof ( key ) * 8 ;
/* write to holding buffer */
buf_printf ( & out , " %s \n " , fmt ) ;
/* zero memory which held key component (will be freed by GC) */
memset ( fmt , 0 , strlen ( fmt ) ) ;
CLEAR ( key ) ;
}
buf_printf ( & out , " %s \n " , static_key_foot ) ;
/* write number of bits */
2010-03-31 08:38:21 +02:00
buf_printf ( & nbits_head_text , " # \n # %d bit OpenVPN static key \n # \n " , nbits ) ;
2005-09-26 07:28:27 +02:00
buf_write_string_file ( & nbits_head_text , filename , fd ) ;
/* write key file, now formatted in out, to file */
buf_write_string_file ( & out , filename , fd ) ;
if ( close ( fd ) )
msg ( M_ERR , " Close error on shared secret file %s " , filename ) ;
/* zero memory which held file content (memory will be freed by GC) */
buf_clear ( & out ) ;
/* pop our garbage collection level */
gc_free ( & gc ) ;
return nbits ;
}
void
must_have_n_keys ( const char * filename , const char * option , const struct key2 * key2 , int n )
{
if ( key2 - > n < n )
{
# ifdef ENABLE_SMALL
msg ( M_FATAL , " Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] " , filename , option , key2 - > n , n ) ;
# else
msg ( M_FATAL , " Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with ' " PACKAGE " --genkey --secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter " , filename , option , key2 - > n , n , option ) ;
# endif
}
}
int
ascii2keydirection ( int msglevel , const char * str )
{
if ( ! str )
return KEY_DIRECTION_BIDIRECTIONAL ;
else if ( ! strcmp ( str , " 0 " ) )
return KEY_DIRECTION_NORMAL ;
else if ( ! strcmp ( str , " 1 " ) )
return KEY_DIRECTION_INVERSE ;
else
{
msg ( msglevel , " Unknown key direction '%s' -- must be '0' or '1' " , str ) ;
return - 1 ;
}
return KEY_DIRECTION_BIDIRECTIONAL ; /* NOTREACHED */
}
const char *
keydirection2ascii ( int kd , bool remote )
{
if ( kd = = KEY_DIRECTION_BIDIRECTIONAL )
return NULL ;
else if ( kd = = KEY_DIRECTION_NORMAL )
return remote ? " 1 " : " 0 " ;
else if ( kd = = KEY_DIRECTION_INVERSE )
return remote ? " 0 " : " 1 " ;
else
{
ASSERT ( 0 ) ;
}
return NULL ; /* NOTREACHED */
}
void
key_direction_state_init ( struct key_direction_state * kds , int key_direction )
{
CLEAR ( * kds ) ;
switch ( key_direction )
{
case KEY_DIRECTION_NORMAL :
kds - > out_key = 0 ;
kds - > in_key = 1 ;
kds - > need_keys = 2 ;
break ;
case KEY_DIRECTION_INVERSE :
kds - > out_key = 1 ;
kds - > in_key = 0 ;
kds - > need_keys = 2 ;
break ;
case KEY_DIRECTION_BIDIRECTIONAL :
kds - > out_key = 0 ;
kds - > in_key = 0 ;
kds - > need_keys = 1 ;
break ;
default :
ASSERT ( 0 ) ;
}
}
void
verify_fix_key2 ( struct key2 * key2 , const struct key_type * kt , const char * shared_secret_file )
{
int i ;
for ( i = 0 ; i < key2 - > n ; + + i )
{
/* Fix parity for DES keys and make sure not a weak key */
fixup_key ( & key2 - > keys [ i ] , kt ) ;
/* This should be a very improbable failure */
if ( ! check_key ( & key2 - > keys [ i ] , kt ) )
msg ( M_FATAL , " Key #%d in '%s' is bad. Try making a new key with --genkey. " ,
i + 1 , shared_secret_file ) ;
}
}
/* given a key and key_type, write key to buffer */
bool
write_key ( const struct key * key , const struct key_type * kt ,
struct buffer * buf )
{
ASSERT ( kt - > cipher_length < = MAX_CIPHER_KEY_LENGTH
& & kt - > hmac_length < = MAX_HMAC_KEY_LENGTH ) ;
if ( ! buf_write ( buf , & kt - > cipher_length , 1 ) )
return false ;
if ( ! buf_write ( buf , & kt - > hmac_length , 1 ) )
return false ;
if ( ! buf_write ( buf , key - > cipher , kt - > cipher_length ) )
return false ;
if ( ! buf_write ( buf , key - > hmac , kt - > hmac_length ) )
return false ;
return true ;
}
/*
* Given a key_type and buffer , read key from buffer .
* Return : 1 on success
* - 1 read failure
* 0 on key length mismatch
*/
int
read_key ( struct key * key , const struct key_type * kt , struct buffer * buf )
{
uint8_t cipher_length ;
uint8_t hmac_length ;
CLEAR ( * key ) ;
if ( ! buf_read ( buf , & cipher_length , 1 ) )
goto read_err ;
if ( ! buf_read ( buf , & hmac_length , 1 ) )
goto read_err ;
if ( ! buf_read ( buf , key - > cipher , cipher_length ) )
goto read_err ;
if ( ! buf_read ( buf , key - > hmac , hmac_length ) )
goto read_err ;
if ( cipher_length ! = kt - > cipher_length | | hmac_length ! = kt - > hmac_length )
goto key_len_err ;
return 1 ;
read_err :
msg ( D_TLS_ERRORS , " TLS Error: error reading key from remote " ) ;
return - 1 ;
key_len_err :
msg ( D_TLS_ERRORS ,
" TLS Error: key length mismatch, local cipher/hmac %d/%d, remote cipher/hmac %d/%d " ,
kt - > cipher_length , kt - > hmac_length , cipher_length , hmac_length ) ;
return 0 ;
}
/*
* Enable crypto acceleration , if available
*/
static bool engine_initialized = false ; /* GLOBAL */
# if CRYPTO_ENGINE
static ENGINE * engine_persist = NULL ; /* GLOBAL */
/* Try to load an engine in a shareable library */
static ENGINE *
try_load_engine ( const char * engine )
{
ENGINE * e = ENGINE_by_id ( " dynamic " ) ;
if ( e )
{
if ( ! ENGINE_ctrl_cmd_string ( e , " SO_PATH " , engine , 0 )
| | ! ENGINE_ctrl_cmd_string ( e , " LOAD " , NULL , 0 ) )
{
ENGINE_free ( e ) ;
e = NULL ;
}
}
return e ;
}
static ENGINE *
setup_engine ( const char * engine )
{
ENGINE * e = NULL ;
ENGINE_load_builtin_engines ( ) ;
if ( engine )
{
if ( strcmp ( engine , " auto " ) = = 0 )
{
msg ( M_INFO , " Initializing OpenSSL auto engine support " ) ;
ENGINE_register_all_complete ( ) ;
return NULL ;
}
if ( ( e = ENGINE_by_id ( engine ) ) = = NULL
& & ( e = try_load_engine ( engine ) ) = = NULL )
{
msg ( M_FATAL , " OpenSSL error: cannot load engine '%s' " , engine ) ;
}
if ( ! ENGINE_set_default ( e , ENGINE_METHOD_ALL ) )
{
msg ( M_FATAL , " OpenSSL error: ENGINE_set_default failed on engine '%s' " ,
engine ) ;
}
msg ( M_INFO , " Initializing OpenSSL support for engine '%s' " ,
ENGINE_get_id ( e ) ) ;
}
return e ;
}
# endif
void
init_crypto_lib_engine ( const char * engine_name )
{
if ( ! engine_initialized )
{
# if CRYPTO_ENGINE
ASSERT ( engine_name ) ;
ASSERT ( ! engine_persist ) ;
engine_persist = setup_engine ( engine_name ) ;
# else
msg ( M_WARN , " Note: OpenSSL hardware crypto engine functionality is not available " ) ;
# endif
engine_initialized = true ;
}
}
/*
* This routine should have additional OpenSSL crypto library initialisations
* used by both crypto and ssl components of OpenVPN .
*/
void init_crypto_lib ( )
{
}
void uninit_crypto_lib ( )
{
# if CRYPTO_ENGINE
if ( engine_initialized )
{
ENGINE_cleanup ( ) ;
engine_persist = NULL ;
engine_initialized = false ;
}
# endif
2008-11-18 02:25:05 +01:00
prng_uninit ( ) ;
2005-09-26 07:28:27 +02:00
}
/*
* Random number functions , used in cases where we want
* reasonably strong cryptographic random number generation
* without depleting our entropy pool . Used for random
* IV values and a number of other miscellaneous tasks .
*/
2008-11-18 02:25:05 +01:00
static uint8_t * nonce_data ; /* GLOBAL */
static const EVP_MD * nonce_md = NULL ; /* GLOBAL */
static int nonce_secret_len ; /* GLOBAL */
2005-09-26 07:28:27 +02:00
2008-11-18 02:25:05 +01:00
void
prng_init ( const char * md_name , const int nonce_secret_len_parm )
{
prng_uninit ( ) ;
nonce_md = md_name ? get_md ( md_name ) : NULL ;
if ( nonce_md )
{
ASSERT ( nonce_secret_len_parm > = NONCE_SECRET_LEN_MIN & & nonce_secret_len_parm < = NONCE_SECRET_LEN_MAX ) ;
nonce_secret_len = nonce_secret_len_parm ;
{
const int size = EVP_MD_size ( nonce_md ) + nonce_secret_len ;
dmsg ( D_CRYPTO_DEBUG , " PRNG init md=%s size=%d " , EVP_MD_name ( nonce_md ) , size ) ;
nonce_data = ( uint8_t * ) malloc ( size ) ;
check_malloc_return ( nonce_data ) ;
# if 1 /* Must be 1 for real usage */
2011-06-22 17:16:03 +02:00
if ( ! rand_bytes ( nonce_data , size ) )
2008-11-18 02:25:05 +01:00
msg ( M_FATAL , " ERROR: Random number generator cannot obtain entropy for PRNG " ) ;
# else
/* Only for testing -- will cause a predictable PRNG sequence */
{
int i ;
for ( i = 0 ; i < size ; + + i )
nonce_data [ i ] = ( uint8_t ) i ;
}
# endif
}
}
}
2005-09-26 07:28:27 +02:00
void
2008-11-18 02:25:05 +01:00
prng_uninit ( void )
2005-09-26 07:28:27 +02:00
{
2008-11-18 02:25:05 +01:00
free ( nonce_data ) ;
nonce_data = NULL ;
nonce_md = NULL ;
nonce_secret_len = 0 ;
2005-09-26 07:28:27 +02:00
}
void
prng_bytes ( uint8_t * output , int len )
{
2008-11-18 02:25:05 +01:00
if ( nonce_md )
2005-09-26 07:28:27 +02:00
{
2008-11-18 02:25:05 +01:00
EVP_MD_CTX ctx ;
const int md_size = EVP_MD_size ( nonce_md ) ;
while ( len > 0 )
{
unsigned int outlen = 0 ;
const int blen = min_int ( len , md_size ) ;
EVP_DigestInit ( & ctx , nonce_md ) ;
EVP_DigestUpdate ( & ctx , nonce_data , md_size + nonce_secret_len ) ;
EVP_DigestFinal ( & ctx , nonce_data , & outlen ) ;
ASSERT ( outlen = = md_size ) ;
EVP_MD_CTX_cleanup ( & ctx ) ;
memcpy ( output , nonce_data , blen ) ;
output + = blen ;
len - = blen ;
}
2005-09-26 07:28:27 +02:00
}
2008-11-18 02:25:05 +01:00
else
2011-06-22 17:16:03 +02:00
rand_bytes ( output , len ) ;
2005-09-26 07:28:27 +02:00
}
/* an analogue to the random() function, but use prng_bytes */
long int
get_random ( )
{
long int l ;
prng_bytes ( ( unsigned char * ) & l , sizeof ( l ) ) ;
if ( l < 0 )
l = - l ;
return l ;
}
const char *
md5sum ( uint8_t * buf , int len , int n_print_chars , struct gc_arena * gc )
{
uint8_t digest [ MD5_DIGEST_LENGTH ] ;
MD5 ( buf , len , digest ) ;
return format_hex ( digest , MD5_DIGEST_LENGTH , n_print_chars , gc ) ;
}
/*
* OpenSSL memory debugging . If dmalloc debugging is enabled , tell
* OpenSSL to use our private malloc / realloc / free functions so that
* we can dispatch them to dmalloc .
*/
# ifdef DMALLOC
static void *
crypto_malloc ( size_t size , const char * file , int line )
{
return dmalloc_malloc ( file , line , size , DMALLOC_FUNC_MALLOC , 0 , 0 ) ;
}
static void *
crypto_realloc ( void * ptr , size_t size , const char * file , int line )
{
return dmalloc_realloc ( file , line , ptr , size , DMALLOC_FUNC_REALLOC , 0 ) ;
}
static void
crypto_free ( void * ptr )
{
dmalloc_free ( __FILE__ , __LINE__ , ptr , DMALLOC_FUNC_FREE ) ;
}
void
openssl_dmalloc_init ( void )
{
CRYPTO_set_mem_ex_functions ( crypto_malloc ,
crypto_realloc ,
crypto_free ) ;
}
# endif /* DMALLOC */
# ifndef USE_SSL
void
init_ssl_lib ( void )
{
ERR_load_crypto_strings ( ) ;
OpenSSL_add_all_algorithms ( ) ;
init_crypto_lib ( ) ;
}
void
free_ssl_lib ( void )
{
uninit_crypto_lib ( ) ;
EVP_cleanup ( ) ;
ERR_free_strings ( ) ;
}
# endif /* USE_SSL */
2009-09-27 04:12:15 +02:00
/*
* md5 functions
*/
void
md5_state_init ( struct md5_state * s )
{
MD5_Init ( & s - > ctx ) ;
}
void
md5_state_update ( struct md5_state * s , void * data , size_t len )
{
MD5_Update ( & s - > ctx , data , len ) ;
}
void
md5_state_final ( struct md5_state * s , struct md5_digest * out )
{
MD5_Final ( out - > digest , & s - > ctx ) ;
}
void
md5_digest_clear ( struct md5_digest * digest )
{
CLEAR ( * digest ) ;
}
bool
md5_digest_defined ( const struct md5_digest * digest )
{
int i ;
for ( i = 0 ; i < MD5_DIGEST_LENGTH ; + + i )
if ( digest - > digest [ i ] )
return true ;
return false ;
}
bool
md5_digest_equal ( const struct md5_digest * d1 , const struct md5_digest * d2 )
{
return memcmp ( d1 - > digest , d2 - > digest , MD5_DIGEST_LENGTH ) = = 0 ;
}
2005-09-26 07:28:27 +02:00
# endif /* USE_CRYPTO */