libmoost
/home/mhx/git/github/libmoost/include/moost/nagios/detail/nsca_crypto.hpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 #ifndef MOOST_NAGIOS_NSCA_CRYPTO_HPP__
00029 #define MOOST_NAGIOS_NSCA_CRYPTO_HPP__
00030 
00031 #include <string>
00032 #include <stdexcept>
00033 
00034 #ifdef HAVE_LIBMCRYPT
00035 #include <mcrypt.h>
00036 #endif
00037 
00038 #include <boost/scoped_ptr.hpp>
00039 #include <boost/noncopyable.hpp>
00040 
00041 #include "nsca_common.hpp"
00042 #include "nsca_data_packet.hpp"
00043 
00044 namespace moost{ namespace nagios{
00045 
00046 #ifdef WIN32
00047    inline void bzero(void * p, size_t const size)
00048    {
00049       memset(p, 0, size);
00050    }
00051 #endif
00052 
00053    class nsca_crypto: boost::noncopyable
00054    {
00055    private:
00056       struct crypt_instance{
00057          char transmitted_iv[nsca_const::TRANSMITTED_IV_SIZE];
00058 #ifdef HAVE_LIBMCRYPT
00059          MCRYPT td;
00060          std::vector<char> key;
00061          std::vector<char> IV;
00062          char block_buffer;
00063          int blocksize;
00064          size_t keysize;
00065          char *mcrypt_algorithm;
00066          char *mcrypt_mode;
00067 #endif
00068       };
00069 
00070       typedef boost::scoped_ptr< crypt_instance > crypt_instance_ptr;
00071 
00072    public:
00073       nsca_crypto(
00074          char const * received_iv,
00075          int const method,
00076          std::string const & password
00077          )
00078          : method_(method)
00079          , password_(password)
00080          , CIptr_(new crypt_instance)
00081       {
00082          encrypt_init(password_.c_str(), method, received_iv, CIptr_);
00083       }
00084 
00085       ~nsca_crypto()
00086       {
00087          encrypt_cleanup(method_, CIptr_);
00088       }
00089 
00090       void encrypt(nsca_data_packet & packet) const
00091       {
00092          encrypt_buffer(
00093             reinterpret_cast<char *>(&packet), sizeof(packet),
00094             password_.c_str(), method_, CIptr_);
00095       }
00096 
00097       void decrypt(nsca_data_packet & packet) const
00098       {
00099          decrypt_buffer(
00100             reinterpret_cast<char *>(&packet), sizeof(packet),
00101             password_.c_str(), method_, CIptr_);
00102       }
00103 
00104    private:
00105       //---> NASTY CODE ALERT
00106       // [ricky 7/14/2011] hoiked (and cleaned up a tad) from nsca_send
00107       //
00108 #ifdef HAVE_LIBMCRYPT
00109          void encrypt_init(char const *password,int encryption_method,char const * received_iv, crypt_instance_ptr &CI) const
00110 #else // shut up gcc moaning that password and encryption_method are not used
00111          void encrypt_init(char const *,int encryption_method,char const * received_iv, crypt_instance_ptr &CI) const
00112 #endif
00113          {
00114 
00115          /* server generates IV used for encryption */
00116          if(received_iv==NULL)
00117             throw std::runtime_error("initialization vector not defined");
00118 
00119          /* client recieves IV from server */
00120          memcpy(CI->transmitted_iv,received_iv,nsca_const::TRANSMITTED_IV_SIZE);
00121 
00122 #ifdef HAVE_LIBMCRYPT
00123          CI->blocksize=1; /* block size = 1 byte w/ CFB mode */
00124          CI->keysize=7; /* default to 56 bit key length */
00125          CI->mcrypt_mode="cfb"; /* CFB = 8-bit cipher-feedback mode */
00126          CI->mcrypt_algorithm="unknown";
00127 #endif
00128 
00129          /* XOR or no encryption */
00130          if(encryption_method==nsca_encryption_method::ENCRYPT_NONE || encryption_method==nsca_encryption_method::ENCRYPT_XOR)
00131             return;
00132 
00133 #ifdef HAVE_LIBMCRYPT
00134 
00135          /* get the name of the mcrypt encryption algorithm to use */
00136          switch(encryption_method){
00137    case nsca_encryption_method::ENCRYPT_DES:
00138       CI->mcrypt_algorithm=MCRYPT_DES;
00139       break;
00140    case nsca_encryption_method::ENCRYPT_3DES:
00141       CI->mcrypt_algorithm=MCRYPT_3DES;
00142       break;
00143    case nsca_encryption_method::ENCRYPT_CAST128:
00144       CI->mcrypt_algorithm=MCRYPT_CAST_128;
00145       break;
00146    case nsca_encryption_method::ENCRYPT_CAST256:
00147       CI->mcrypt_algorithm=MCRYPT_CAST_256;
00148       break;
00149    case nsca_encryption_method::ENCRYPT_XTEA:
00150       CI->mcrypt_algorithm=MCRYPT_XTEA;
00151       break;
00152    case nsca_encryption_method::ENCRYPT_3WAY:
00153       CI->mcrypt_algorithm=MCRYPT_3WAY;
00154       break;
00155    case nsca_encryption_method::ENCRYPT_BLOWFISH:
00156       CI->mcrypt_algorithm=MCRYPT_BLOWFISH;
00157       break;
00158    case nsca_encryption_method::ENCRYPT_TWOFISH:
00159       CI->mcrypt_algorithm=MCRYPT_TWOFISH;
00160       break;
00161    case nsca_encryption_method::ENCRYPT_LOKI97:
00162       CI->mcrypt_algorithm=MCRYPT_LOKI97;
00163       break;
00164    case nsca_encryption_method::ENCRYPT_RC2:
00165       CI->mcrypt_algorithm=MCRYPT_RC2;
00166       break;
00167    case nsca_encryption_method::ENCRYPT_ARCFOUR:
00168       CI->mcrypt_algorithm=MCRYPT_ARCFOUR;
00169       break;
00170    case nsca_encryption_method::ENCRYPT_RIJNDAEL128:
00171       CI->mcrypt_algorithm=MCRYPT_RIJNDAEL_128;
00172       break;
00173    case nsca_encryption_method::ENCRYPT_RIJNDAEL192:
00174       CI->mcrypt_algorithm=MCRYPT_RIJNDAEL_192;
00175       break;
00176    case nsca_encryption_method::ENCRYPT_RIJNDAEL256:
00177       CI->mcrypt_algorithm=MCRYPT_RIJNDAEL_256;
00178       break;
00179    case nsca_encryption_method::ENCRYPT_WAKE:
00180       CI->mcrypt_algorithm=MCRYPT_WAKE;
00181       break;
00182    case nsca_encryption_method::ENCRYPT_SERPENT:
00183       CI->mcrypt_algorithm=MCRYPT_SERPENT;
00184       break;
00185    case nsca_encryption_method::ENCRYPT_ENIGMA:
00186       CI->mcrypt_algorithm=MCRYPT_ENIGMA;
00187       break;
00188    case nsca_encryption_method::ENCRYPT_GOST:
00189       CI->mcrypt_algorithm=MCRYPT_GOST;
00190       break;
00191    case nsca_encryption_method::ENCRYPT_SAFER64:
00192       CI->mcrypt_algorithm=MCRYPT_SAFER_SK64;
00193       break;
00194    case nsca_encryption_method::ENCRYPT_SAFER128:
00195       CI->mcrypt_algorithm=MCRYPT_SAFER_SK128;
00196       break;
00197    case nsca_encryption_method::ENCRYPT_SAFERPLUS:
00198       CI->mcrypt_algorithm=MCRYPT_SAFERPLUS;
00199       break;
00200 
00201    default:
00202       CI->mcrypt_algorithm="unknown";
00203       break;
00204          }
00205 
00206          /* open encryption module */
00207          if((CI->td=mcrypt_module_open(CI->mcrypt_algorithm,NULL,CI->mcrypt_mode,NULL))==MCRYPT_FAILED){
00208             throw std::runtime_error("Could not open mcrypt algorithm");
00209          }
00210 
00211          /* determine size of IV buffer for this algorithm */
00212          size_t iv_size=mcrypt_enc_get_iv_size(CI->td);
00213          if(iv_size>nsca_const::TRANSMITTED_IV_SIZE){
00214             throw std::runtime_error("IV size for crypto algorithm exceeds limits");
00215          }
00216 
00217          CI->IV.resize(iv_size);
00218 
00219          /* fill IV buffer with first bytes of IV that is going to be used to crypt (determined by server) */
00220          for(size_t i=0;i<iv_size;i++)
00221             CI->IV[i]=CI->transmitted_iv[i];
00222 
00223          /* get maximum key size for this algorithm */
00224          CI->keysize=mcrypt_enc_get_key_size(CI->td);
00225 
00226          CI->key.resize(CI->keysize);
00227 
00228          bzero(&CI->key[0],CI->keysize);
00229 
00230          if(CI->keysize<strlen(password))
00231             strncpy(&CI->key[0],password,CI->keysize);
00232          else
00233             strncpy(&CI->key[0],password,strlen(password));
00234 
00235          /* initialize encryption buffers */
00236          mcrypt_generic_init(CI->td,&CI->key[0],CI->keysize,&CI->IV[0]);
00237 
00238 #endif
00239       }
00240 
00241 
00242 
00243       /* encryption routine cleanup */
00244 #ifdef HAVE_LIBMCRYPT
00245       void encrypt_cleanup(int encryption_method, crypt_instance_ptr const & CI)
00246 #else // shut up gcc moaning encryption_method is not used
00247       void encrypt_cleanup(int, crypt_instance_ptr const & CI)
00248 #endif
00249       {
00250 
00251          /* no crypt instance */
00252          if(!CI)
00253             return;
00254 
00255 #ifdef HAVE_LIBMCRYPT
00256          /* mcrypt cleanup */
00257          if(encryption_method!=nsca_encryption_method::ENCRYPT_NONE && encryption_method!=nsca_encryption_method::ENCRYPT_XOR){
00258             mcrypt_generic_end(CI->td);
00259          }
00260 #endif
00261       }
00262 
00263       /* encrypt a buffer */
00264       void encrypt_buffer(char *buffer,size_t buffer_size, char const *password, int encryption_method, crypt_instance_ptr const & CI) const
00265       {
00266          size_t x;
00267          size_t y;
00268          size_t password_length;
00269 
00270          /* no crypt instance */
00271          if(!CI)
00272             return;
00273 
00274          /* no encryption */
00275          if(encryption_method==nsca_encryption_method::ENCRYPT_NONE)
00276             return;
00277 
00278          /* simple XOR "encryption" - not meant for any real security, just obfuscates data, but its fast... */
00279          else if(encryption_method==nsca_encryption_method::ENCRYPT_XOR){
00280 
00281             /* rotate over IV we received from the server... */
00282             for(y=0,x=0;y<buffer_size;y++,x++){
00283 
00284                /* keep rotating over IV */
00285                if(x>=nsca_const::TRANSMITTED_IV_SIZE)
00286                   x=0;
00287 
00288                buffer[y]^=CI->transmitted_iv[x];
00289             }
00290 
00291             /* rotate over password... */
00292             password_length=strlen(password);
00293             for(y=0,x=0;y<buffer_size;y++,x++){
00294 
00295                /* keep rotating over password */
00296                if(x>=password_length)
00297                   x=0;
00298 
00299                buffer[y]^=password[x];
00300             }
00301 
00302             return;
00303          }
00304 
00305 #ifdef HAVE_LIBMCRYPT
00306          /* use mcrypt routines */
00307          else{
00308 
00309             /* encrypt each byte of buffer, one byte at a time (CFB mode) */
00310             for(x=0;x<buffer_size;x++)
00311                mcrypt_generic(CI->td,&buffer[x],1);
00312          }
00313 #endif
00314 
00315          return;
00316       }
00317 
00318 
00319       /* decrypt a buffer */
00320       void decrypt_buffer(char *buffer,size_t buffer_size, char const*password, int encryption_method, crypt_instance_ptr const &CI) const
00321       {
00322 
00323          /* no crypt instance */
00324          if(CI==NULL)
00325             return;
00326 
00327          /* no encryption */
00328          if(encryption_method==nsca_encryption_method::ENCRYPT_NONE)
00329             return;
00330 
00331          /* XOR "decryption" is the same as encryption */
00332          else if(encryption_method==nsca_encryption_method::ENCRYPT_XOR)
00333             encrypt_buffer(buffer,buffer_size,password,encryption_method,CI);
00334 
00335 #ifdef HAVE_LIBMCRYPT
00336          /* use mcrypt routines */
00337          else{
00338 
00339             /* encrypt each byte of buffer, one byte at a time (CFB mode) */
00340             for(size_t x=0;x<buffer_size;x++)
00341                mdecrypt_generic(CI->td,&buffer[x],1);
00342          }
00343 #endif
00344 
00345          return;
00346       }
00347       // ---<
00348 
00349       int method_;
00350       std::string password_;
00351       crypt_instance_ptr CIptr_;
00352    };
00353 
00354 }}
00355 
00356 #endif