www.pudn.com > javasnmpv3.rar > auth_priv.cpp


/*_############################################################################
  _## 
  _##  auth_priv.cpp  
  _##
  _##  SNMP++v3.2.22
  _##  -----------------------------------------------
  _##  Copyright (c) 2001-2007 Jochen Katz, Frank Fock
  _##
  _##  This software is based on SNMP++2.6 from Hewlett Packard:
  _##  
  _##    Copyright (c) 1996
  _##    Hewlett-Packard Company
  _##  
  _##  ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS.
  _##  Permission to use, copy, modify, distribute and/or sell this software 
  _##  and/or its documentation is hereby granted without fee. User agrees 
  _##  to display the above copyright notice and this license notice in all 
  _##  copies of the software and any documentation of the software. User 
  _##  agrees to assume all liability for the use of the software; 
  _##  Hewlett-Packard and Jochen Katz make no representations about the 
  _##  suitability of this software for any purpose. It is provided 
  _##  "AS-IS" without warranty of any kind, either express or implied. User 
  _##  hereby grants a royalty-free license to any and all derivatives based
  _##  upon this software code base. 
  _##  
  _##  Stuttgart, Germany, Wed May  2 23:22:30 CEST 2007 
  _##  
  _##########################################################################*/
char auth_priv_version[]="@(#) SNMP++ $Id: auth_priv.cpp 256 2006-06-03 20:16:00Z katz $";

#include "snmp_pp/config_snmp_pp.h"

#ifdef _SNMPv3

#include 
#include 
#include 

// Only use DES, AES, SHA1 and MD5 from libtomcrypt if openssl is not used
#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
#include 
#endif

// Use DES, AES, SHA and MD5 from openssl
#ifdef _USE_OPENSSL
#include 
#include 
#include 
#include 
#endif

// Use internal functions for SHA and MD5 and libdes only
// if not using libtomcrypt and openssl
#if !defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
#include "snmp_pp/sha.h"
#ifdef RSAEURO
#include 
#else
#include 
#include "snmp_pp/md5.h"
#endif
#endif // !defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)

// IDEA can only be used with a valid license
#ifdef _USE_IDEA
#include "snmp_pp/idea.h"
#endif

#include "snmp_pp/auth_priv.h"
#include "snmp_pp/v3.h"
#include "snmp_pp/snmperrs.h"
#include "snmp_pp/address.h"
#include "snmp_pp/log.h"

#ifdef SNMP_PP_NAMESPACE
namespace Snmp_pp {
#endif

/*-----------------[ defines for crypto libraries ]------------------*/

#ifdef _USE_OPENSSL

/* -- START: Defines for OpenSSL -- */
typedef SHA_CTX               SHAHashStateType;
#define SHA1_INIT(s)          SHA1_Init(s)
#define SHA1_PROCESS(s, p, l) SHA1_Update(s, p, l)
#define SHA1_DONE(s, k)       SHA1_Final(k, s)

typedef MD5_CTX               MD5HashStateType;
#define MD5_INIT(s)           MD5_Init(s)
#define MD5_PROCESS(s, p, l)  MD5_Update(s, p, l)
#define MD5_DONE(s, k)        MD5_Final(k, s)

typedef des_key_schedule      DESCBCType;
#define DES_CBC_START_ENCRYPT(c, iv, k, kl, r, s) \
                 if (des_key_sched((C_Block*)(k), s) < 0) \
                 { \
		   debugprintf(0, "Starting DES encryption failed."); \
		   return SNMPv3_USM_ERROR; \
                 }
#define DES_CBC_START_DECRYPT(c, iv, k, kl, r, s) \
                 if (des_key_sched((C_Block*)(k), s) < 0) \
                 { \
		   debugprintf(0, "Starting DES decryption failed."); \
		   return SNMPv3_USM_ERROR; \
                 }

#define DES_CBC_ENCRYPT(pt, ct, s, iv, l) \
                        des_ncbc_encrypt(pt, ct, l, \
                                         s, (C_Block*)(iv), DES_ENCRYPT)
#define DES_CBC_DECRYPT(ct, pt, s, iv, l) \
                        des_ncbc_encrypt(ct, pt, l, \
                                         s, (C_Block*)(iv), DES_DECRYPT)

#define DES_EDE3_CBC_ENCRYPT(pt, ct, l, k1, k2, k3, iv) \
               des_ede3_cbc_encrypt(pt, ct, l, \
                                    k1, k2, k3, (C_Block*)(iv), DES_ENCRYPT)

#define DES_EDE3_CBC_DECRYPT(ct, pt, l, k1, k2, k3, iv) \
               des_ede3_cbc_encrypt(ct, pt, l, \
                                    k1, k2, k3, (C_Block*)(iv), DES_DECRYPT)

#define DES_MEMSET(s, c, l)   memset(&(s), c, l)

/* -- END: Defines for OpenSSL -- */

#else

#ifdef _USE_LIBTOMCRYPT

/* -- START: Defines for LibTomCrypt -- */
typedef hash_state            SHAHashStateType;
#define SHA1_INIT(s)          sha1_init(s)
#define SHA1_PROCESS(s, p, l) sha1_process(s, p, l)
#define SHA1_DONE(s, k)       sha1_done(s, k)

typedef hash_state            MD5HashStateType;
#define MD5_INIT(s)           md5_init(s)
#define MD5_PROCESS(s, p, l)  md5_process(s, p, l)
#define MD5_DONE(s, k)        md5_done(s, k)

typedef symmetric_CBC         DESCBCType;
#define DES_CBC_START_ENCRYPT(c, iv, k, kl, r, s) \
                 if (cbc_start(c, iv, k, kl, r, &(s)) != CRYPT_OK) \
                 { \
		   debugprintf(0, "Starting DES encryption failed."); \
		   return SNMPv3_USM_ERROR; \
                 }

#define DES_CBC_START_DECRYPT(c, iv, k, kl, r, s) \
                 if (cbc_start(c, iv, k, kl, r, &(s)) != CRYPT_OK) \
                 { \
		   debugprintf(0, "Starting DES decryption failed."); \
		   return SNMPv3_USM_ERROR; \
                 }

#define DES_CBC_ENCRYPT(pt, ct, s, iv, l) \
                 if (cbc_encrypt(pt, ct, l, &(s)) != CRYPT_OK) \
                 { \
		   debugprintf(0, "Error during DES encryption."); \
		   return SNMPv3_USM_ERROR; \
                 }
#define DES_CBC_DECRYPT(ct, pt, s, iv, l) \
                 if (cbc_decrypt(ct, pt, l, &(s)) != CRYPT_OK) \
                 { \
		   debugprintf(0, "Error during DES decryption."); \
		   return SNMPv3_USM_ERROR; \
                 }
#define DES_MEMSET(s, c, l)   memset(&(s), c, l)
/* -- END: Defines for LibTomCrypt -- */

#else // _USE_LIBTOMCRYPT  --> libdes

/* -- START: Defines for libdes -- */

typedef SHA_CTX               SHAHashStateType;
#define SHA1_INIT(s)          SHAInit(s)
#define SHA1_PROCESS(s, p, l) SHAUpdate(s, p, l)
#define SHA1_DONE(s, k)       SHAFinal(k, s)

typedef MD5_CTX               MD5HashStateType;
#define MD5_INIT(s)           MD5Init(s)
#define MD5_PROCESS(s, p, l)  MD5Update(s, p, l)
#define MD5_DONE(s, k)        MD5Final(k, s)

#define DES_EDE3_CBC_ENCRYPT(pt, ct, l, k1, k2, k3, iv) \
               des_ede3_cbc_encrypt((C_Block*)(pt), (C_Block*)(ct), l, \
                                    k1, k2, k3, (C_Block*)(iv), DES_ENCRYPT)

#define DES_EDE3_CBC_DECRYPT(ct, pt, l, k1, k2, k3, iv) \
               des_ede3_cbc_encrypt((C_Block*)(ct), (C_Block*)(pt), l, \
                                    k1, k2, k3, (C_Block*)(iv), DES_DECRYPT)

#ifdef RSAEURO

#undef  MD5_PROCESS
#define MD5_PROCESS(s, p, l)  MD5Update(s, (unsigned char*)(p), l)

typedef DES_CBC_CTX           DESCBCType;
#define DES_CBC_START_ENCRYPT(c, iv, k, kl, r, s) \
                              DES_CBCInit(&(s), (unsigned char*)(k), iv, 1)
#define DES_CBC_START_DECRYPT(c, iv, k, kl, r, s) \
                              DES_CBCInit(&(s),(unsigned char*)(k), iv, 0)
#define DES_CBC_ENCRYPT(pt, ct, s, iv, l) DES_CBCUpdate(&(s), pt, ct, l)
#define DES_CBC_DECRYPT(ct, pt, s, iv, l) DES_CBCUpdate(&(s), (unsigned char*)(ct), pt, l)
#define DES_MEMSET(s, c, l)   R_memset((POINTER)&(s), c, l)

#else // RSAEURO

typedef des_key_schedule      DESCBCType;
#define DES_CBC_START_ENCRYPT(c, iv, k, kl, r, s) \
                 if (des_key_sched((C_Block*)(k), s) < 0) \
                 { \
		   debugprintf(0, "Starting DES encryption failed."); \
		   return SNMPv3_USM_ERROR; \
                 }
#define DES_CBC_START_DECRYPT(c, iv, k, kl, r, s) \
                 if (des_key_sched((C_Block*)(k), s) < 0) \
                 { \
		   debugprintf(0, "Starting DES decryption failed."); \
		   return SNMPv3_USM_ERROR; \
                 }

#define DES_CBC_ENCRYPT(pt, ct, s, iv, l) \
                        des_ncbc_encrypt((C_Block*)(pt), (C_Block*)(ct), l, \
                                         s, (C_Block*)(iv), DES_ENCRYPT)
#define DES_CBC_DECRYPT(ct, pt, s, iv, l) \
                        des_ncbc_encrypt((C_Block*)(ct), (C_Block*)(pt), l, \
                                         s, (C_Block*)(iv), DES_DECRYPT)
#define DES_MEMSET(s, c, l)   memset(&(s), c, l)

/* -- END: Defines for libdes -- */

#endif // RSAEURO

#endif // _USE_LIBTOMCRYPT

#endif // _USE_OPENSSL

AuthPriv::AuthPriv(int &construct_state)
{
  auth = new AuthPtr[10];
  priv = new PrivPtr[10];

  if (auth)
    auth_size = 10;
  else
  {
    auth_size = 0;

    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error allocating array for authentication.");
    LOG_END;
  }

  if (priv)
    priv_size = 10;
  else
  {
    priv_size = 0;

    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error allocating array for privacy.");
    LOG_END;
  }

  for (int i = 0; i < auth_size; i++)
    auth[i] = 0;

  for (int j = 0; j < priv_size; j++)
    priv[j] = 0;

  /* Check size of salt, has to be 64 bits */
  if (sizeof(salt) != 8)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: *BUG* sizeof(pp_uint64) is not 8 bytes. snmp++ has to be patched for this system.");
    LOG_END;

    construct_state = SNMPv3_USM_ERROR;
    return;
  }

  /* Initialize salt. srand() has been already done in Snmp::init() */
  unsigned int *rnd = (unsigned int*)&salt;
  *rnd = rand() << 1;
  if (rand() < (RAND_MAX / 2))
    *rnd += 1;
  rnd++;
  *rnd = rand() << 1;
  if (rand() < (RAND_MAX / 2))
    *rnd += 1;

  construct_state = SNMPv3_USM_OK;

#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
  /* register needed hashes and ciphers in libtomcrypt */
  if (register_cipher(&rijndael_desc) < 0)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error registering Rijndael.");
    LOG_END;

    construct_state = SNMPv3_USM_ERROR;
  }

  if (register_cipher(&des_desc) < 0)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error registering DES.");
    LOG_END;

    construct_state = SNMPv3_USM_ERROR;
  }

  if (register_cipher(&des3_desc) < 0)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error registering 3DES.");
    LOG_END;

    construct_state = SNMPv3_USM_ERROR;
  }

  if (register_hash(&sha1_desc) < 0)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error registering SHA1.");
    LOG_END;

    construct_state = SNMPv3_USM_ERROR;
  }

  if (register_hash(&md5_desc) < 0)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Error registering MD5.");
    LOG_END;

    construct_state = SNMPv3_USM_ERROR;
  }
#endif // defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
}

AuthPriv::~AuthPriv()
{
  for (int i = 0; i < auth_size; i++)
    if (auth[i])
    {
      delete auth[i];
      auth[i] = 0;
    }

  for (int j = 0; j < priv_size; j++)
    if (priv[j])
    {
      delete priv[j];
      priv[j] = 0;
    }

  delete [] auth;
  delete [] priv;
}

int AuthPriv::add_auth(Auth *new_auth)
{
  if (!new_auth)
  {
    return SNMP_CLASS_ERROR;
  }

  int id = new_auth->get_id();

  if (id < 0)
  {
    return SNMP_CLASS_ERROR;
  }

  if (id >= auth_size)
  {
    AuthPtr *new_array = new AuthPtr[id + 5];
    if (!new_array)
    {
      LOG_BEGIN(ERROR_LOG | 1);
      LOG("AuthPriv: Could not allocate new auth array.");
      LOG_END;

      return SNMP_CLASS_ERROR;
    }
    for (int i=0 ; iset_salt(&salt);

  if (auth[id])
  {
    LOG_BEGIN(WARNING_LOG | 4);
    LOG("AuthPriv: deleting old auth object before adding new one (id)");
    LOG(id);
    LOG_END;

    delete auth[id];
  }

  auth[id] = new_auth;

  LOG_BEGIN(INFO_LOG | 6);
  LOG("AuthPriv: Added auth protocol (id)");
  LOG(id);
  LOG_END;

  return SNMP_CLASS_SUCCESS;
}

int AuthPriv::del_auth(const int auth_id)
{
  if ((auth_id < 0) || (auth_id >= auth_size) || (auth[auth_id] == 0))
  {
    LOG_BEGIN(WARNING_LOG | 4);
    LOG("AuthPriv: Request to delete non existing auth protocol (id)");
    LOG(auth_id);
    LOG_END;

    return SNMP_CLASS_ERROR;
  }

  delete auth[auth_id];
  auth[auth_id] = 0;

  LOG_BEGIN(INFO_LOG | 6);
  LOG("AuthPriv: Removed auth protocol (id)");
  LOG(auth_id);
  LOG_END;

  return SNMP_CLASS_SUCCESS;
}


int AuthPriv::add_priv(Priv *new_priv)
{
  if (!new_priv)
  {
    return SNMP_CLASS_ERROR;
  }

  int id = new_priv->get_id();

  if (id < 0)
  {
    return SNMP_CLASS_ERROR;
  }

  if (id >= priv_size)
  {
    PrivPtr *new_array = new PrivPtr[id + 5];
    if (!new_array)
    {
      LOG_BEGIN(ERROR_LOG | 1);
      LOG("AuthPriv: Could not allocate new priv array.");
      LOG_END;

      return SNMP_CLASS_ERROR;
    }
    for (int i=0 ; iset_salt(&salt);

  if (priv[id])
  {
    LOG_BEGIN(WARNING_LOG | 4);
    LOG("AuthPriv: deleting old priv object before adding new one (id)");
    LOG(id);
    LOG_END;

    delete priv[id];
  }

  priv[id] = new_priv;

  LOG_BEGIN(INFO_LOG | 6);
  LOG("AuthPriv: Added priv protocol (id)");
  LOG(id);
  LOG_END;

  return SNMP_CLASS_SUCCESS;
}

int AuthPriv::del_priv(const int priv_id)
{
  if ((priv_id < 0) || (priv_id >= priv_size) || (priv[priv_id] == 0))
  {
    LOG_BEGIN(WARNING_LOG | 4);
    LOG("AuthPriv: Request to delete non existing priv protocol (id)");
    LOG(priv_id);
    LOG_END;

    return SNMP_CLASS_ERROR;
  }

  delete priv[priv_id];
  priv[priv_id] = 0;

  LOG_BEGIN(INFO_LOG | 6);
  LOG("AuthPriv: Removed priv protocol (id)");
  LOG(priv_id);
  LOG_END;

  return SNMP_CLASS_SUCCESS;
}

Auth *AuthPriv::get_auth(const int auth_prot)
{
  if ((auth_prot >= 0) && (auth_prot < auth_size))
    return auth[auth_prot];
  return 0;
}

Priv *AuthPriv::get_priv(const int priv_prot)
{
  if ((priv_prot >= 0) && (priv_prot < priv_size))
    return priv[priv_prot];
  return 0;
}

// Get the unique id for the given auth protocol.
int AuthPriv::get_auth_id(const char *string_id) const
{
  for (int i = 0; i < auth_size; ++i)
    if ((auth[i]) && (strcmp(string_id, auth[i]->get_id_string()) == 0))
      return i;
  return -1;
}

// Get the unique id for the given priv protocol.
int AuthPriv::get_priv_id(const char *string_id) const
{
  for (int i = 0; i < priv_size; ++i)
    if ((priv[i]) && (strcmp(string_id, priv[i]->get_id_string()) == 0))
      return i;
  return -1;
}

int AuthPriv::get_keychange_value(const int       auth_prot,
                                  const OctetStr& old_key,
                                  const OctetStr& new_key,
                                  OctetStr&       keychange_value)
{

  // uses fixed key length determined from oldkey!
  // works with SHA and MD5
  // modifications needed to support variable length keys
  // algorithm according to USM-document textual convention KeyChange

  int key_len = old_key.len();

  Auth *a = get_auth(auth_prot);

  if (!a)
    return SNMPv3_USM_UNSUPPORTED_AUTHPROTOCOL;

  // compute random value
  OctetStr random = "";

  for (int i=0; ihash(tmp.data(), tmp.len(), digest);

  // step 4:
  keychange_value = random;
  keychange_value += new_key;
  for (unsigned int j = key_len; j < keychange_value.len(); j++) {
    keychange_value[j] = keychange_value[j] ^ digest[j - key_len];
  }

#ifdef __DEBUG
  debughexcprintf(21, "keychange_value",
                  keychange_value.data(), keychange_value.len());
#endif

  return SNMPv3_USM_OK;
}

int AuthPriv::password_to_key_auth(const int            auth_prot,
                                   const unsigned char *password,
                                   const unsigned int   password_len,
                                   const unsigned char *engine_id,
                                   const unsigned int   engine_id_len,
                                   unsigned char *key,
                                   unsigned int  *key_len)
{
  if (auth_prot == SNMP_AUTHPROTOCOL_NONE)
  {
    *key_len = 0;
    return SNMPv3_USM_OK;
  }

  if (!password || (password_len == 0))
  {
    LOG_BEGIN(WARNING_LOG | 2);
    LOG("AuthPriv: Password to key auth needs a non empty password");
    LOG_END;

    return SNMPv3_USM_ERROR;
  }

  Auth *a = get_auth(auth_prot);

  if (!a)
    return SNMPv3_USM_UNSUPPORTED_AUTHPROTOCOL;

  int res = a->password_to_key(password, password_len,
                               engine_id, engine_id_len,
                               key, key_len);

  return res;
}


int AuthPriv::password_to_key_priv(const int            auth_prot,
                                   const int            priv_prot,
                                   const unsigned char *password,
                                   const unsigned int   password_len,
                                   const unsigned char *engine_id,
                                   const unsigned int   engine_id_len,
                                   unsigned char *key,
                                   unsigned int  *key_len)
{
  /* check for priv protocol */
  if (priv_prot == SNMP_PRIVPROTOCOL_NONE)
  {
    *key_len = 0;
    return SNMPv3_USM_OK;
  }

  if (!password || (password_len == 0))
  {
    LOG_BEGIN(WARNING_LOG | 2);
    LOG("AuthPriv: Password to key priv needs a non empty password");
    LOG_END;

    return SNMPv3_USM_ERROR;
  }

  Priv *p = get_priv(priv_prot);
  Auth *a = get_auth(auth_prot);

  if (!p)  return SNMPv3_USM_UNSUPPORTED_PRIVPROTOCOL;
  if (!a)  return SNMPv3_USM_UNSUPPORTED_AUTHPROTOCOL;

  unsigned int max_key_len = *key_len; /* save length of buffer! */
  unsigned int min_key_len = p->get_min_key_len();

  /* check if buffer for key is long enough */
  if (min_key_len > max_key_len)
    return SNMPv3_USM_ERROR; // TODO: better error code!

  int res = password_to_key_auth(auth_prot,
				 password, password_len,
				 engine_id, engine_id_len,
				 key, key_len);
  if (res != SNMPv3_USM_OK)
    return res;

  /* We have a too short key: Call priv protocoll to extend it */
  if (*key_len < min_key_len)
  {
    res = p->extend_short_key(password, password_len,
			      engine_id, engine_id_len,
			      key, key_len, max_key_len, a);
    if (res != SNMPv3_USM_OK)
      return res;
  }

  /* make sure key length is valid */
  p->fix_key_len(*key_len);

  return SNMPv3_USM_OK;
}




int AuthPriv::encrypt_msg(const int            priv_prot,
                          const unsigned char *key,
                          const unsigned int   key_len,
                          const unsigned char *buffer,
                          const unsigned int   buffer_len,
                          unsigned char       *out_buffer,
                          unsigned int        *out_buffer_len,
                          unsigned char       *privacy_params,
                          unsigned int        *privacy_params_len,
                          const unsigned long  engine_boots,
                          const unsigned long  engine_time)
{
  /* check for priv protocol */
  Priv *p = get_priv(priv_prot);

  if (!p)
    return SNMPv3_USM_UNSUPPORTED_PRIVPROTOCOL;

  return p->encrypt(key, key_len, buffer, buffer_len,
                    out_buffer, out_buffer_len,
                    privacy_params, privacy_params_len,
                    engine_boots, engine_time);
}

int AuthPriv::decrypt_msg(const int            priv_prot,
                          const unsigned char *key,
                          const unsigned int   key_len,
                          const unsigned char *buffer,
                          const unsigned int   buffer_len,
                          unsigned char       *out_buffer,
                          unsigned int        *out_buffer_len,
                          const unsigned char *privacy_params,
                          const unsigned int   privacy_params_len,
			  const unsigned long  engine_boots,
			  const unsigned long  engine_time)
{
  /* check for priv protocol */
  Priv *p = get_priv(priv_prot);

  if (!p)
    return SNMPv3_USM_UNSUPPORTED_PRIVPROTOCOL;

  return p->decrypt(key, key_len, buffer, buffer_len,
                    out_buffer, out_buffer_len,
                    privacy_params, privacy_params_len,
		    engine_boots, engine_time);
}


int AuthPriv::add_default_modules()
{
  int ret = SNMP_CLASS_SUCCESS;

  if (add_auth(new AuthSHA()) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol AuthSHA.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }

  if (add_auth(new AuthMD5()) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol AuthMD5.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }

  if (add_priv(new PrivDES()) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol PrivDES.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }

#ifdef _USE_IDEA
  if (add_priv(new PrivIDEA()) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol PrivIDEA.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }
#endif

#if defined(_USE_LIBTOMCRYPT) || defined(_USE_OPENSSL)
  if (add_priv(new PrivAES(SNMP_PRIVPROTOCOL_AES128)) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol PrivAES 128.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }

  if (add_priv(new PrivAES(SNMP_PRIVPROTOCOL_AES192)) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol PrivAES 192.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }

  if (add_priv(new PrivAES(SNMP_PRIVPROTOCOL_AES256)) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol PrivAES 256.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }
#endif

#ifdef _USE_3DES_EDE
  if (add_priv(new Priv3DES_EDE()) != SNMP_ERROR_SUCCESS)
  {
    LOG_BEGIN(ERROR_LOG | 1);
    LOG("AuthPriv: Could not add default protocol Priv3DES_EDE.");
    LOG_END;

    ret = SNMP_CLASS_ERROR;
  }
#endif


  if (ret == SNMP_CLASS_SUCCESS)
  {
    LOG_BEGIN(INFO_LOG | 3);
    LOG("AuthPriv: Added default Auth and Priv protocols.");
    LOG_END;
  }

  return ret;
}

int AuthPriv::get_auth_params_len(const int auth_prot)
{
  Auth *a = get_auth(auth_prot);

  if (!a)
    return 0;

  return a->get_auth_params_len();
}

int AuthPriv::get_priv_params_len(const int priv_prot)
{
  Priv *p = get_priv(priv_prot);

  if (!p)
    return 0;

  return p->get_priv_params_len();
}

int AuthPriv::auth_out_msg(const int            auth_prot,
                           const unsigned char *key,
                           unsigned char       *msg,
                           const int            msg_len,
                           unsigned char       *auth_par_ptr)
{
  if (auth_prot == SNMP_AUTHPROTOCOL_NONE)
    return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;

  Auth *a = get_auth(auth_prot);

  if (!a)
    return SNMPv3_USM_UNSUPPORTED_AUTHPROTOCOL;

  return a->auth_out_msg(key, msg, msg_len, auth_par_ptr);
}

int AuthPriv::auth_inc_msg(const int            auth_prot,
                           const unsigned char *key,
                           unsigned char       *msg,
                           const int            msg_len,
                           unsigned char       *auth_par_ptr,
                           const int            auth_par_len)
{
  if (auth_prot == SNMP_AUTHPROTOCOL_NONE)
    return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;

  Auth *a = get_auth(auth_prot);

  if (!a)
    return SNMPv3_USM_UNSUPPORTED_AUTHPROTOCOL;

  /* @todo check if auth par is inside msg
  if ((auth_par_ptr < msg) ||
      (msg + msg_len < auth_par_ptr + auth_par_len))
  {
    LOG_BEGIN(WARNING_LOG | 1);
    LOG("AuthPriv: Authentication data is not within message (msg start) (len) (auth start) (len)");
    LOG(msg);
    LOG(msg_len);
    LOG(auth_par_ptr);
    LOG(auth_par_len);
    LOG_END;

    return SNMPv3_USM_ERROR;
  }
  */

  return a->auth_inc_msg(key, msg, msg_len, auth_par_ptr, auth_par_len);
}

/* ========================================================== */

/* ----------------------- AuthSHA ---------------------------------------*/

int AuthSHA::password_to_key(const unsigned char *password,
                             const unsigned int   password_len,
                             const unsigned char *engine_id,
                             const unsigned int   engine_id_len,
                             unsigned char *key,
                             unsigned int *key_len)
{
  *key_len = 20; /* All SHA keys have 20 bytes length */

#ifdef __DEBUG
  debugprintf(5,"password_to_key SHA: password: (%s).",
	      OctetStr(password, password_len).get_printable());
  debugprintf(5,"password_to_key SHA: engine_id: (%s).",
	      OctetStr(engine_id, engine_id_len).get_printable());
#endif

  SHAHashStateType sha_hash_state;
  unsigned char *cp, password_buf[72];
  unsigned long  password_index = 0;
  unsigned long  count = 0, i;

  SHA1_INIT(&sha_hash_state);   /* initialize SHA */

  /**********************************************/
  /* Use while loop until we've done 1 Megabyte */
  /**********************************************/
  while (count < 1048576) {
    cp = password_buf;
    for (i = 0; i < 64; i++) {
      /*************************************************/
      /* Take the next octet of the password, wrapping */
      /* to the beginning of the password as necessary.*/
      /*************************************************/
      *cp++ = password[password_index++ % password_len];
    }

    SHA1_PROCESS(&sha_hash_state, password_buf, 64);
    count += 64;
  }

  SHA1_DONE(&sha_hash_state, key);          /* tell SHA we're done */

#ifdef __DEBUG
  debughexcprintf(21, "key", key, *key_len);
#endif

  /*****************************************************/
  /* Now localize the key with the engine_id and pass  */
  /* through SHA to produce final key                  */
  /* May want to ensure that engine_id_len <= 32,      */
  /* otherwise need to use a buffer larger than 72     */
  /*****************************************************/
  memcpy(password_buf,                            key,       *key_len);
  memcpy(password_buf + *key_len,                 engine_id, engine_id_len);
  memcpy(password_buf + *key_len + engine_id_len, key,       *key_len);

  SHA1_INIT(&sha_hash_state);
  SHA1_PROCESS(&sha_hash_state, password_buf, (2 * *key_len) + engine_id_len);
  SHA1_DONE(&sha_hash_state, key);

#ifdef __DEBUG
  debughexcprintf(21, "localized key", key, *key_len);
#endif

  return SNMPv3_USM_OK;
}

int AuthSHA::hash(const unsigned char *data,
                  const unsigned int   data_len,
                  unsigned char       *digest) const
{
  SHAHashStateType sha_hash_state;

  SHA1_INIT(&sha_hash_state);
  SHA1_PROCESS(&sha_hash_state, data, data_len);
  SHA1_DONE(&sha_hash_state, digest);

  return SNMPv3_USM_OK;
}

int AuthSHA::auth_out_msg(const unsigned char *key,
                          unsigned char *msg,
                          const int msg_len,
                          unsigned char *auth_par_ptr)
{
  SHAHashStateType sha_hash_state;
  int           key_len = 20; /* We use only 20 Byte Key! */
  unsigned char digest[20];
  unsigned char k_ipad[65];   /* inner padding - key XORd with ipad */
  unsigned char k_opad[65];   /* outer padding - key XORd with opad */

  memset((char*)(auth_par_ptr), 0, 12);

#ifdef __DEBUG
  debughexcprintf(21, "key", key, 16);
#endif

  /*
   * the HMAC_SHA transform looks like:
   *
   * SHA(K XOR opad, SHA(K XOR ipad, msg))
   *
   * where K is an n byte key
   * ipad is the byte 0x36 repeated 64 times
   * opad is the byte 0x5c repeated 64 times
   * and text is the data being protected
   */

  /* start out by storing ipads and opads in pads */
  memset( (char*)k_ipad, 0x36, sizeof k_ipad);
  memset( (char*)k_opad, 0x5c, sizeof k_opad);

  /* XOR pads with key */
  for (int i=0; i < key_len; ++i) {
    k_ipad[i] ^= key[i];
    k_opad[i] ^= key[i];
  }

  /* perform inner SHA */
  SHA1_INIT(&sha_hash_state);           /* init sha_hash_state for 1st pass */
  SHA1_PROCESS(&sha_hash_state, k_ipad, 64);   /* start with inner pad      */
  SHA1_PROCESS(&sha_hash_state, msg, msg_len); /* then text of datagram     */
  SHA1_DONE(&sha_hash_state, digest);          /* finish up 1st pass        */
  /* perform outer SHA */
  SHA1_INIT(&sha_hash_state);           /* init sha_hash_state for 2nd pass */
  SHA1_PROCESS(&sha_hash_state, k_opad, 64);   /* start with outer pad      */
  SHA1_PROCESS(&sha_hash_state, digest, 20);   /* then results of 1st hash  */
  SHA1_DONE(&sha_hash_state, digest);          /* finish up 2nd pass        */

#ifdef __DEBUG
  debughexcprintf(21,"digest", digest, 160 / 8);
#endif

  memcpy(auth_par_ptr, digest, 12);

  return SNMPv3_USM_OK;
}


int AuthSHA::auth_inc_msg(const unsigned char *key,
                          unsigned char *msg,
                          const int msg_len,
                          unsigned char *auth_par_ptr,
                          const int      auth_par_len)
{
  unsigned char receivedDigest[20];

  if (auth_par_len != 12)
  {
    debugprintf(4, "SHA illegal digest length (%d), authentication FAILED.",
		auth_par_len);
    return SNMPv3_USM_AUTHENTICATION_FAILURE;
  }

#ifdef __DEBUG
  debughexcprintf(21, "digest in Message", auth_par_ptr, 12);
  debughexcprintf(21, "key", key, 20);
#endif

  /* Save received digest */
  memcpy(receivedDigest, auth_par_ptr, 12);

  if (SNMPv3_USM_OK != auth_out_msg(key, msg, msg_len, auth_par_ptr))
  {
    /* copy digest back into message and return error */
    memcpy(auth_par_ptr, receivedDigest, 12);
    debugprintf(4, "SHA authentication FAILED (1).");
    return SNMPv3_USM_AUTHENTICATION_FAILURE;
  }

  /* compare digest to received digest */
  for (int i=0; i < 12 ; ++i)
  {
    if (auth_par_ptr[i] != receivedDigest[i])
    {
      /* copy digest back into message and return error */
      memcpy(auth_par_ptr, receivedDigest, 12);
      debugprintf(4, "SHA authentication FAILED.");
      return SNMPv3_USM_AUTHENTICATION_FAILURE;
    }
  }
  debugprintf(4, "SHA authentication OK.");
  return SNMPv3_USM_OK;
}


/* ----------------------- AuthMD5 ---------------------------------------*/

int AuthMD5::password_to_key(const unsigned char *password,
                             const unsigned int   password_len,
                             const unsigned char *engine_id,
                             const unsigned int   engine_id_len,
                             unsigned char *key,
                             unsigned int *key_len)
{
  *key_len = 16; /* All MD5 keys have 16 bytes length */

#ifdef __DEBUG
  debugprintf(5,"password: %s.",
              OctetStr(password, password_len).get_printable());
  debugprintf(5,"engineID: %s.",
              OctetStr(engine_id, engine_id_len).get_printable());
#endif

  MD5HashStateType md5_hash_state;
  unsigned char  *cp, password_buf[65];
  unsigned long   password_index = 0;
  unsigned long   count = 0, i;

  MD5_INIT(&md5_hash_state);   /* initialize MD5 */

  /**********************************************/
  /* Use while loop until we've done 1 Megabyte */
  /**********************************************/
  while (count < 1048576) {
    cp = password_buf;
    for (i = 0; i < 64; i++) {
      /*************************************************/
      /* Take the next octet of the password, wrapping */
      /* to the beginning of the password as necessary.*/
      /*************************************************/
      *cp++ = password[password_index++ % password_len];
    }
    MD5_PROCESS(&md5_hash_state, password_buf, 64);
    count += 64;
  }
  MD5_DONE(&md5_hash_state, key);      /* tell MD5 we're done */

#ifdef __DEBUG
  debughexcprintf(21, "key", key, *key_len);
#endif

  /*****************************************************/
  /* Now localize the key with the engine_id and pass  */
  /* through MD5 to produce final key                  */
  /* May want to ensure that engine_id_len <= 32,      */
  /* otherwise need to use a buffer larger than 64     */
  /*****************************************************/
  memcpy(password_buf,                            key,       *key_len);
  memcpy(password_buf + *key_len,                 engine_id, engine_id_len);
  memcpy(password_buf + *key_len + engine_id_len, key,       *key_len);

  MD5_INIT(&md5_hash_state);
  MD5_PROCESS(&md5_hash_state, password_buf, (2 * *key_len) + engine_id_len);
  MD5_DONE(&md5_hash_state, key);

#ifdef __DEBUG
  debughexcprintf(21, "localized key", key, *key_len);
#endif

  return SNMPv3_USM_OK;
}

int AuthMD5::hash(const unsigned char *data,
                  const unsigned int   data_len,
                  unsigned char       *digest) const
{
  MD5HashStateType md5_hash_state;

  MD5_INIT(&md5_hash_state);
  MD5_PROCESS(&md5_hash_state, data, data_len);
  MD5_DONE(&md5_hash_state, digest);

  return SNMPv3_USM_OK;
}

int AuthMD5::auth_out_msg(const unsigned char *key,
                          unsigned char *msg,
                          const int      msg_len,
                          unsigned char *auth_par_ptr)
{
  MD5HashStateType md5_hash_state;
  int           key_len = 16; /* We use only 16 Byte Key! */
  unsigned char digest[16];
  unsigned char k_ipad[65];   /* inner padding - key XORd with ipad */
  unsigned char k_opad[65];   /* outer padding - key XORd with opad */

  memset((char*)(auth_par_ptr), 0, 12);

#ifdef __DEBUG
  debughexcprintf(21, "key", key, 16);
#endif

  /*
   * the HMAC_MD5 transform looks like:
   *
   * MD5(K XOR opad, MD5(K XOR ipad, msg))
   *
   * where K is an n byte key
   * ipad is the byte 0x36 repeated 64 times
   * opad is the byte 0x5c repeated 64 times
   * and text is the data being protected
   */

  /* start out by storing key in pads */
  memset( (char*)k_ipad, 0, sizeof k_ipad);
  memset( (char*)k_opad, 0, sizeof k_opad);
  memcpy( (char*)k_ipad, (char*)key, key_len);
  memcpy( (char*)k_opad, (char*)key, key_len);

  /* XOR key with ipad and opad values */
  for (int i=0; i<64; i++) {
    k_ipad[i] ^= 0x36;
    k_opad[i] ^= 0x5c;
  }

  /* perform inner MD5 */
  MD5_INIT(&md5_hash_state);            /* init md5_hash_state for 1st pass */
  MD5_PROCESS(&md5_hash_state, k_ipad, 64);    /* start with inner pad      */
  MD5_PROCESS(&md5_hash_state, msg, msg_len);  /* then text of datagram     */
  MD5_DONE(&md5_hash_state, digest);           /* finish up 1st pass        */
  /* perform outer MD5 */
  MD5_INIT(&md5_hash_state);            /* init md5_hash_state for 2nd pass */
  MD5_PROCESS(&md5_hash_state, k_opad, 64);    /* start with outer pad      */
  MD5_PROCESS(&md5_hash_state, digest, 16);    /* then results of 1st hash  */
  MD5_DONE(&md5_hash_state, digest);           /* finish up 2nd pass        */

#ifdef __DEBUG
  debughexcprintf(21, "digest", digest, 128 / 8);
#endif

  memcpy(auth_par_ptr, digest, 12);

  return SNMPv3_USM_OK;
}

int AuthMD5::auth_inc_msg(const unsigned char *key,
                          unsigned char *msg,
                          const int msg_len,
                          unsigned char *auth_par_ptr,
                          const int      auth_par_len)
{
  unsigned char receivedDigest[16];

  if (auth_par_len != 12)
  {
    debugprintf(4, "MD5 illegal digest length (%d), authentication FAILED.",
		auth_par_len);
    return SNMPv3_USM_AUTHENTICATION_FAILURE;
  }

#ifdef __DEBUG
  debughexcprintf(21, "digest in Message", auth_par_ptr, 12);
  debughexcprintf(21, "key", key, 16);
#endif

  memcpy(receivedDigest, auth_par_ptr, 12);

  if (SNMPv3_USM_OK != auth_out_msg(key, msg, msg_len, auth_par_ptr))
  {
    /* copy digest back into message and return error */
    memcpy(auth_par_ptr, receivedDigest, 12);
    debugprintf(4, "MD5 authentication FAILED (1).");
    return SNMPv3_USM_AUTHENTICATION_FAILURE;
  }

  /* compare digest to received digest */
  for (int i=0; i < 12 ; ++i)
  {
    if (auth_par_ptr[i] != receivedDigest[i])
    {
      /* copy digest back into message and return error */
      memcpy(auth_par_ptr, receivedDigest, 12);
      debugprintf(4, "MD5 authentication FAILED.");
      return SNMPv3_USM_AUTHENTICATION_FAILURE;
    }
  }
  debugprintf(4, "MD5 authentication OK.");
  return SNMPv3_USM_OK;
}

/* ========================= PRIV ================================*/

/* ----------------------- PrivDES ---------------------------------------*/

#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
PrivDES::PrivDES()
{
  cipher = find_cipher("des");
}
#endif

int PrivDES::encrypt(const unsigned char *key,
                     const unsigned int   /*key_len*/,
                     const unsigned char *buffer,
                     const unsigned int   buffer_len,
                     unsigned char       *out_buffer,
                     unsigned int        *out_buffer_len,
                     unsigned char       *privacy_params,
                     unsigned int        *privacy_params_len,
                     const unsigned long  engine_boots,
                     const unsigned long  /*engine_time*/)
{
  unsigned char initVect[8];
  pp_uint64     my_salt = (*salt)++;

#ifdef INVALID_ENCRYPTION
  debugprintf(-10, "\nWARNING: Encrypting with zeroed salt!\n");
  my_salt = 0;
#endif

  /* check space in privacy_params buffer */
  if (*privacy_params_len < 8)
  {
    debugprintf(4, "Buffer too small: should be 8, is (%i).",
                *privacy_params_len);
    return SNMPv3_USM_ENCRYPTION_ERROR;
  }
  /* Length is always 8 */
  *privacy_params_len = 8;

  // last 8 bytes of key are used as base for initialization vector
  memcpy((char*)initVect, key+8, 8);

  // put salt in privacy_params
  for (int j=0; j<4; j++)
  {
    privacy_params[3-j] = (unsigned char) (0xFF & (engine_boots >> (8*j)));
    privacy_params[7-j] = (unsigned char) (0xFF & (my_salt >> (8*j)));
  }

  // xor initVect with salt
  for (int i=0; i<8; i++)
    initVect[i] ^= privacy_params[i];

#ifdef __DEBUG
  debughexcprintf(21, "apDESEncryptData: Data to encrypt",
                 buffer, buffer_len);
  debughexcprintf(21, "apDESEncryptData: used key (only 8 bytes used)",
                  key, 16);
  debughexcprintf(21, "apDESEncryptData: used iv",
                  initVect, 8);
#endif

  DESCBCType symcbc;
  DES_CBC_START_ENCRYPT(cipher, initVect, key, 8, 16, symcbc);

  for(unsigned int k = 0; k <= buffer_len - 8; k += 8) {
    DES_CBC_ENCRYPT(buffer + k, out_buffer + k, symcbc, initVect, 8);
  }

  /* last part of buffer */
  if (buffer_len % 8)
  {
    unsigned char tmp_buf[8];
    unsigned char *tmp_buf_ptr = tmp_buf;
    int start = buffer_len - (buffer_len % 8);
    memset(tmp_buf, 0, 8);
    for (unsigned int l = start; l < buffer_len; l++)
      *tmp_buf_ptr++ = buffer[l];
    DES_CBC_ENCRYPT(tmp_buf, out_buffer + start, symcbc, initVect, 8);
    *out_buffer_len = buffer_len + 8 - (buffer_len % 8);
  }
  else
    *out_buffer_len = buffer_len;

  /* Clear context buffer (paranoia!)*/
  DES_MEMSET(symcbc, 0, sizeof(symcbc));

#ifdef __DEBUG
  debughexcprintf(21, "apDESEncryptData: created privacy_params",
                  privacy_params, 8);
  debughexcprintf(21, "apDESEncryptData: encrypted Data",
                  out_buffer, *out_buffer_len);
#endif

  return SNMPv3_USM_OK;
}



int PrivDES::decrypt(const unsigned char *key,
                     const unsigned int   /*key_len*/,
                     const unsigned char *buffer,
                     const unsigned int   buffer_len,
                     unsigned char *outBuffer,
                     unsigned int  *outBuffer_len,
                     const unsigned char *privacy_params,
                     const unsigned int   privacy_params_len,
		     const unsigned long  /*engine_boots*/,
		     const unsigned long  /*engine_time*/)
{
  unsigned char initVect[8];

  /* Privacy params length has to be 8  && Length has to be a multiple of 8 */
  if (( buffer_len % 8 ) || (privacy_params_len != 8))
    return SNMPv3_USM_DECRYPTION_ERROR;

  for (int i=0; i<8; i++)
    initVect[i] = privacy_params[i] ^ key[i+8];

  memset((char*)outBuffer, 0, *outBuffer_len);

#ifdef __DEBUG
  debughexcprintf(21, "apDESDecryptData: Data to decrypt",
                  buffer, buffer_len);
  debughexcprintf(21, "apDESDecryptData: used key (only 8 bytes used)",
                  key, 16);
  debughexcprintf(21, "apDESDecryptData: used privacy_params",
                  privacy_params, 8);
  debughexcprintf(21, "apDESDecryptData: used iv",
                  initVect, 8);
#endif

  DESCBCType symcbc;
  DES_CBC_START_DECRYPT(cipher, initVect, key, 8, 16, symcbc);
  for(unsigned int j=0; j> (8*j)));
    privacy_params[7-j] = (unsigned char) (0xFF & (my_salt >> (8*j)));
  }
  // xor iv with privacy_params
  for (int i=0; i<8; i++)
    iv[i] ^= privacy_params[i];

  idea_set_key(&CFB_Context, key);

  idea_cfb_encrypt(&CFB_Context, iv, out_buffer,
                   buffer, buffer_len);

  /* Clear context (paranoia!) */
  idea_destroy_context(&CFB_Context);

  *out_buffer_len = buffer_len;

#ifdef __DEBUG
  debughexcprintf(21, "apIDEAEncryptData: Data to encrypt",
                  buffer, buffer_len);
  debughexcprintf(21, "apIDEAEncryptData: key",
                  key, 16);
  debughexcprintf(21, "apIDEAEncryptData: privacy_params",
                  privacy_params, 8);
  debughexcprintf(21, "apIDEAEncryptData: encrypted Data",
                  out_buffer, *out_buffer_len);
#endif

  return SNMPv3_USM_OK;
}

int PrivIDEA::decrypt(const unsigned char *key,
                      const unsigned int   /*key_len*/,
                      const unsigned char *buffer,
                      const unsigned int   buffer_len,
                      unsigned char *out_buffer,
                      unsigned int  *out_buffer_len,
                      const unsigned char *privacy_params,
                      const unsigned int   privacy_params_len,
		      const unsigned long  /*engine_boots*/,
		      const unsigned long  /*engine_time*/)
{
  unsigned char iv[8];
  IDEAContext CFB_Context;

  /* privacy params length has to be 8 */
  if (privacy_params_len != 8)
    return SNMPv3_USM_DECRYPTION_ERROR;

  idea_set_key(&CFB_Context, key);

  memset((char*)out_buffer, 0, *out_buffer_len);

  /* Initialize iv with last 8 bytes of key and xor with privacy_params */
  memcpy((char*)iv, key+8, 8);
  for (int i=0; i<8; i++)
    iv[i] ^= privacy_params[i];

  idea_cfb_decrypt(&CFB_Context, iv, out_buffer,
                   buffer, buffer_len);

  /* Clear context (paranoia!) */
  idea_destroy_context(&CFB_Context);
  memset((char*)iv, 0, 8);

  *out_buffer_len = buffer_len;

#ifdef __DEBUG
  debughexcprintf(21, "apIDEADecryptData: Data to decrypt",
                  buffer, buffer_len);
  debughexcprintf(21, "apIDEADecryptData: key", key, 16);
  debughexcprintf(21, "apIDEAEncryptData: privacy_params", privacy_params, 8);
  debughexcprintf(21, "apIDEADecryptData: decrypted Data",
                  out_buffer, *out_buffer_len);
#endif

  return SNMPv3_USM_OK;
}

#endif // _USE_IDEA

#if defined(_USE_LIBTOMCRYPT) || defined(_USE_OPENSSL)

PrivAES::PrivAES(const int aes_type_)
  : aes_type(aes_type_)
{
#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
  cipher = find_cipher("rijndael");
#endif

  switch (aes_type)
  {
    case SNMP_PRIVPROTOCOL_AES128:
      key_bytes = 16;
      rounds = 10;
      break;
    case SNMP_PRIVPROTOCOL_AES192:
      key_bytes = 24;
      rounds = 12;
      break;
    case SNMP_PRIVPROTOCOL_AES256:
      key_bytes = 32;
      rounds = 14;
      break;
    default:
      debugprintf(0, "Wrong AES type: %i.", aes_type);
      key_bytes = 0;
      rounds = 0;
      aes_type = -1; // will cause an error in AuthPriv::add_priv()
  }

  unsigned int testswap = htonl(0x01020304);
  if (testswap == 0x01020304)
    need_byteswap = FALSE;
  else
    need_byteswap = TRUE;
}

const char *PrivAES::get_id_string() const
{
  switch (aes_type)
  {
    case SNMP_PRIVPROTOCOL_AES128: return "AES128";  break;
    case SNMP_PRIVPROTOCOL_AES192: return "AES192";  break;
    case SNMP_PRIVPROTOCOL_AES256: return "AES256";  break;
    default:                       return "error";   break;
  }
};

int PrivAES::encrypt(const unsigned char *key,
		     const unsigned int   key_len,
		     const unsigned char *buffer,
		     const unsigned int   buffer_len,
		     unsigned char       *out_buffer,
		     unsigned int        *out_buffer_len,
		     unsigned char       *privacy_params,
		     unsigned int        *privacy_params_len,
		     const unsigned long  engine_boots,
		     const unsigned long  engine_time)
{
  unsigned char initVect[16];
  pp_uint64 my_salt = (*salt)++;

#ifdef INVALID_ENCRYPTION
  debugprintf(-10, "\nWARNING: Encrypting with zeroed salt!\n");
  my_salt = 0;
#endif

  /* check space in privacy_params buffer */
  if (*privacy_params_len < 8)
  {
    debugprintf(4, "Buffer too small: should be 8, is (%i).",
                *privacy_params_len);
    return SNMPv3_USM_ENCRYPTION_ERROR;
  }
  /* Length is always 8 */
  *privacy_params_len = 8;

  /* Set IV as engine_boots + engine_time + salt */
  unsigned int *tmpi = (unsigned int *)initVect;
  *tmpi++ = htonl(engine_boots);
  *tmpi++ = htonl(engine_time);
  if (need_byteswap)
  {
    *tmpi++ = htonl(my_salt & 0xFFFFFFFF);
    *tmpi   = htonl((my_salt >> 32) & 0xFFFFFFFF);
  }
  else
    memcpy(tmpi, &my_salt, 8);

  /* put byteswapped salt in privacy_params */
  memcpy(privacy_params, initVect + 8, 8);
  debughexcprintf(21, "aes initVect:", initVect, 16);

#ifdef _USE_OPENSSL
  AES_KEY symcfb;
  int dummy = 0;

  if (AES_set_encrypt_key(key, key_len * 8, &symcfb) < 0)
  {
    
    debugprintf(1, "AES_set_encrypt_key(%p, %d, %p) failed.",
		key, key_len * 8, &symcfb);
    return SNMPv3_USM_ERROR;
  }

  AES_cfb128_encrypt(buffer, out_buffer, buffer_len,
		     &symcfb, initVect, &dummy, AES_ENCRYPT);
#else
  symmetric_CFB symcfb;

  cfb_start(cipher, initVect, key, key_bytes, rounds, &symcfb);
  cfb_encrypt((unsigned char*)buffer, out_buffer, buffer_len, &symcfb);
#endif

  /* Clear context and plaintext buffer (paranoia!)*/
  memset(&symcfb, 0, sizeof(symcfb));

  *out_buffer_len = buffer_len;

#ifdef __DEBUG
  debughexcprintf(21, "aes EncryptData: Data to encrypt", buffer, buffer_len);
  debughexcprintf(21, "aes EncryptData: used key", key, key_len);
  debughexcprintf(21, "aes EncryptData: created privacy_params",
                  privacy_params, 8);
  debughexcprintf(21, "aes EncryptData: encrypted Data",
                  out_buffer, *out_buffer_len);
#endif

  return SNMPv3_USM_OK;
}

int PrivAES::decrypt(const unsigned char *key,
		     const unsigned int   key_len,
		     const unsigned char *buffer,
		     const unsigned int   buffer_len,
		     unsigned char       *out_buffer,
		     unsigned int        *out_buffer_len,
		     const unsigned char *privacy_params,
		     const unsigned int   privacy_params_len,
		     const unsigned long  engine_boots,
		     const unsigned long  engine_time)
{
  unsigned char initVect[16];

  /* Privacy params length has to be 8 */
  if (privacy_params_len != 8)
    return SNMPv3_USM_DECRYPTION_ERROR;

  /* build IV */
  unsigned int *tmp;
  tmp = (unsigned int *)initVect;
  *tmp++ = htonl(engine_boots);
  *tmp = htonl(engine_time);
  memcpy(initVect + 8, privacy_params, 8);
  debughexcprintf(21, "aes initVect:", initVect, 16);

#ifdef _USE_OPENSSL
  int dummy = 0;
  AES_KEY symcfb;

  AES_set_encrypt_key(key, key_len * 8, &symcfb);
  AES_cfb128_encrypt(buffer, out_buffer, buffer_len,
		     &symcfb, initVect, &dummy, AES_DECRYPT);
#else
  symmetric_CFB symcfb;

  cfb_start(cipher, initVect, key, key_bytes, rounds, &symcfb);
  cfb_decrypt((unsigned char*)buffer, out_buffer, buffer_len, &symcfb);
#endif

  /* Clear context and plaintext buffer (paranoia!)*/
  memset(&symcfb, 0, sizeof(symcfb));

  *out_buffer_len = buffer_len;

#ifdef __DEBUG
  debughexcprintf(21, "aes DecryptData: Data to decrypt", buffer, buffer_len);
  debughexcprintf(21, "aes DecryptData: used key", key, key_len);
  debughexcprintf(21, "aes DecryptData: used privacy_params",
                  privacy_params, 8);
  debughexcprintf(21, "aes DecryptData: decrypted Data",
                  out_buffer, *out_buffer_len);
#endif

  return SNMPv3_USM_OK;
}

int PrivAES::extend_short_key(const unsigned char *password,
                              const unsigned int   password_len,
                              const unsigned char *engine_id,
                              const unsigned int   engine_id_len,
                              unsigned char       *key,
                              unsigned int        *key_len,
                              const unsigned int   max_key_len,
                              Auth                *auth)
{
  if (max_key_len < (unsigned)key_bytes)
      return SNMPv3_USM_ERROR;

  int res = 0;
  unsigned char *hash_buf = new unsigned char[auth->get_hash_len()];

  if (!hash_buf)
  {
    debugprintf(0, "Out of mem. Did not get %i bytes.", auth->get_hash_len());
    return SNMPv3_USM_ERROR;
  }

  while (*key_len < (unsigned)key_bytes)
  {
    res = auth->hash(key, *key_len, hash_buf);
    if (res != SNMPv3_USM_OK)
      break;

    int copy_bytes = key_bytes - *key_len;
    if (copy_bytes > auth->get_hash_len())
      copy_bytes = auth->get_hash_len();
    if (*key_len + copy_bytes > max_key_len)
	copy_bytes = max_key_len - *key_len;
    memcpy(key + *key_len, hash_buf, copy_bytes);
    *key_len += copy_bytes;
  }

  if (hash_buf) delete [] hash_buf;

  return res;
}


#endif // _USE_LIBTOMCRYPT or _USE_OPENSSL


#ifdef _USE_3DES_EDE

#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
Priv3DES_EDE::Priv3DES_EDE()
{
  cipher = find_cipher("3des");
  debugprintf(10, "tomcrypt returned cipher %d", cipher);
}
#endif


int 
Priv3DES_EDE::encrypt(const unsigned char *key,
		      const unsigned int   key_len,
		      const unsigned char *buffer,
		      const unsigned int   buffer_len,
		      unsigned char       *out_buffer,
		      unsigned int        *out_buffer_len,
		      unsigned char       *privacy_params,
		      unsigned int        *privacy_params_len,
		      const unsigned long  engine_boots,
		      const unsigned long  engine_time)
{
  unsigned char initVect[8];
  pp_uint64     my_salt = (*salt)++;
  
#ifdef INVALID_ENCRYPTION
  debugprintf(-10, "\nWARNING: Encrypting with zeroed salt!\n");
  my_salt = 0;
#endif

  /* check space in privacy_params buffer */
  if (*privacy_params_len < 8)
  {
    debugprintf(4, "Buffer too small: should be 8, is (%i).",
                *privacy_params_len);
    return SNMPv3_USM_ENCRYPTION_ERROR;
  }
  /* Length is always 8 */
  *privacy_params_len = 8;

  /* check key length */
  if (key_len < TRIPLEDES_EDE_KEY_LEN)
  {
    debugprintf(4, "Key too small: should be %d, is (%d).",
                TRIPLEDES_EDE_KEY_LEN, key_len);
    return SNMPv3_USM_ENCRYPTION_ERROR;
  }

  /* TODO: check if K1 != K2 != K3 */

  // last 8 bytes of key are used as base for initialization vector
  memcpy((char*)initVect, key+24, 8);

  /* TODO: generate salt as specified in draft */

  // put salt in privacy_params
  for (int j=0; j<4; j++)
  {
    privacy_params[3-j] = (unsigned char) (0xFF & (engine_boots >> (8*j)));
    privacy_params[7-j] = (unsigned char) (0xFF & (my_salt >> (8*j)));
  }

  // xor initVect with salt
  for (int i=0; i<8; i++)
    initVect[i] ^= privacy_params[i];


#ifdef __DEBUG
  debughexcprintf(21, "3DES Data to encrypt", buffer, buffer_len);
  debughexcprintf(21, "3DES used iv", initVect, 8);
  debughexcprintf(21, "3DES key", key, key_len);
#endif

  // The first 24 octets of the 32-octet secret are used as a 3DES-EDE
  // key. Since 3DES-EDE uses only 168 bits the least significant bit
  // in each octet is disregarded

#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
  DESCBCType symcbc;
  DES_CBC_START_ENCRYPT(cipher, initVect, key, 24, 16, symcbc);

  for(unsigned int k = 0; k <= buffer_len - 8; k += 8) {
    DES_CBC_ENCRYPT(buffer + k, out_buffer + k, symcbc, initVect, 8);
  }

  /* last part of buffer */
  if (buffer_len % 8)
  {
    unsigned char tmp_buf[8];
    unsigned char *tmp_buf_ptr = tmp_buf;
    int start = buffer_len - (buffer_len % 8);
    memset(tmp_buf, 0, 8);
    for (unsigned int l = start; l < buffer_len; l++)
      *tmp_buf_ptr++ = buffer[l];
    DES_CBC_ENCRYPT(tmp_buf, out_buffer + start, symcbc, initVect, 8);
    *out_buffer_len = buffer_len + 8 - (buffer_len % 8);
  }
  else
    *out_buffer_len = buffer_len;

  /* Clear context buffer (paranoia!)*/
  DES_MEMSET(symcbc, 0, sizeof(symcbc));

#else
  DESCBCType ks1, ks2, ks3;

  if ((des_key_sched((C_Block*)(key),     ks1) < 0) ||
      (des_key_sched((C_Block*)(key +8),  ks2) < 0) ||
      (des_key_sched((C_Block*)(key +16), ks3) < 0))
  {
      debugprintf(0, "Starting 3DES-EDE encryption failed.");
      return SNMPv3_USM_ERROR;
  }

  if (buffer_len >= 8)
    for(unsigned int k = 0; k <= (buffer_len - 8); k += 8) 
    {
      DES_EDE3_CBC_ENCRYPT(buffer+k, out_buffer+k, 8,
			   ks1, ks2, ks3, initVect);
    }

  // Last part
  if (buffer_len % 8)
    {
      unsigned char tmp_buf[8];
      unsigned char *tmp_buf_ptr = tmp_buf;
      int start = buffer_len - (buffer_len % 8);
      memset(tmp_buf, 0, 8);
      for (unsigned int l = start; l < buffer_len; l++)
	*tmp_buf_ptr++ = buffer[l];
      DES_EDE3_CBC_ENCRYPT(tmp_buf, out_buffer + start, 8,
			   ks1, ks2, ks3, initVect);
      
      *out_buffer_len = buffer_len + 8 - (buffer_len % 8);
    }
  else
    *out_buffer_len = buffer_len;

  /* Clear context buffer (paranoia!)*/
  DES_MEMSET(ks1, 0, sizeof(ks1));
  DES_MEMSET(ks2, 0, sizeof(ks2));
  DES_MEMSET(ks3, 0, sizeof(ks3));
#endif

#ifdef __DEBUG
  debughexcprintf(21, "3DES created privacy_params", privacy_params, 8);
  debughexcprintf(21, "3DES encrypted Data", out_buffer, *out_buffer_len);
#endif

  return SNMPv3_USM_OK;
}


int 
Priv3DES_EDE::decrypt(const unsigned char *key,
		      const unsigned int   key_len,
		      const unsigned char *buffer,
		      const unsigned int   buffer_len,
		      unsigned char       *out_buffer,
		      unsigned int        *out_buffer_len,
		      const unsigned char *privacy_params,
		      const unsigned int   privacy_params_len,
		      const unsigned long  engine_boots,
		      const unsigned long  engine_time)
{
  unsigned char initVect[8];

  /* Privacy params length has to be 8  && Length has to be a multiple of 8 */
  if (( buffer_len % 8 ) || (privacy_params_len != 8))
    return SNMPv3_USM_DECRYPTION_ERROR;

  for (int i=0; i<8; i++)
    initVect[i] = privacy_params[i] ^ key[i+24];

  memset((char*)out_buffer, 0, *out_buffer_len);

#ifdef __DEBUG
  debughexcprintf(21, "3DES Data to decrypt", buffer, buffer_len);
  debughexcprintf(21, "3DES privacy_params",  privacy_params, 8);
  debughexcprintf(21, "3DES used iv",   initVect, 8);
  debughexcprintf(21, "3DES key", key, key_len);
#endif

#if defined(_USE_LIBTOMCRYPT) && !defined(_USE_OPENSSL)
  DESCBCType symcbc;
  DES_CBC_START_DECRYPT(cipher, initVect, key, 24, 16, symcbc);
  for(unsigned int j=0; jpassword_to_key(key, *key_len,
				engine_id, engine_id_len,
				p2k_buf, &p2k_buf_len);

    if (res != SNMPv3_USM_OK)
      break;

    unsigned int copy_bytes = TRIPLEDES_EDE_KEY_LEN - *key_len;

    if (copy_bytes > p2k_buf_len)
	copy_bytes = p2k_buf_len;

    if (*key_len + copy_bytes > max_key_len)
	copy_bytes = max_key_len - *key_len;

    memcpy(key + *key_len, p2k_buf, copy_bytes);

    *key_len += copy_bytes;
  }

  if (p2k_buf) delete [] p2k_buf;

  return res;
}

#ifdef _TEST
bool Priv3DES_EDE::test()
{
  int status;
  AuthPriv ap(status);
  if (status != SNMPv3_USM_OK)
      return false;

  if (ap.add_auth(new AuthSHA()) != SNMP_ERROR_SUCCESS)
  {
      debugprintf(0, "Error: could not add AuthSHA.");
      return false;
  }

  if (ap.add_auth(new AuthMD5()) != SNMP_ERROR_SUCCESS)
  {
      debugprintf(0, "Error: could not add AuthMD5.");
      return false;
  }

  if (ap.add_priv(new Priv3DES_EDE()) != SNMP_ERROR_SUCCESS)
  {
      debugprintf(0, "Error: could not add Priv3DES_EDE.");
      return false;
  }

  unsigned char password[11] = "maplesyrup";
  unsigned char engine_id[12];

  memset(engine_id, 0, 11);
  engine_id[11] = 2;

  unsigned char key[TRIPLEDES_EDE_KEY_LEN];
  unsigned int key_len = TRIPLEDES_EDE_KEY_LEN;

  status = ap.password_to_key_priv(SNMP_AUTHPROTOCOL_HMACSHA,
                                   SNMP_PRIVPROTOCOL_3DESEDE,
                                   password, 10,
                                   engine_id, 12,
                                   key,  &key_len);

  debughexcprintf(1, "result key 3DES SHA",
                  key, key_len);

  key_len = TRIPLEDES_EDE_KEY_LEN;
  status = ap.password_to_key_priv(SNMP_AUTHPROTOCOL_HMACMD5,
                                   SNMP_PRIVPROTOCOL_3DESEDE,
                                   password, 10,
                                   engine_id, 12,
                                   key,  &key_len);

  debughexcprintf(1, "result key 3DES MD5",
                  key, key_len);

  unsigned char msg[80] = "This is the secret message, that has to be encrypted!";
  unsigned char enc_buffer[80];
  unsigned int enc_buffer_len = 80;
  unsigned char dec_buffer[80];
  unsigned int dec_buffer_len = 80;
  unsigned char priv_params[64];
  unsigned int priv_params_len = 64;


  status = ap.encrypt_msg(SNMP_PRIVPROTOCOL_3DESEDE,
			  key, key_len, msg, 53,
			  enc_buffer, &enc_buffer_len,
			  priv_params, &priv_params_len, 0x5abc, 0x6def);
  
  debughexcprintf(1, "encrypted text",
                  enc_buffer, enc_buffer_len);

  status = ap.decrypt_msg(SNMP_PRIVPROTOCOL_3DESEDE,
			  key, key_len, enc_buffer, enc_buffer_len,
			  dec_buffer, &dec_buffer_len,
			  priv_params, priv_params_len, 0x5abc, 0x6def);

  dec_buffer[dec_buffer_len] = 0;
  debugprintf(1, "decrypted text: %s",
                  dec_buffer);
  // TODO: check keys and return real value
  return true;
}
#endif

#endif // _USE_3DES_EDE


#ifdef SNMP_PP_NAMESPACE
}; // end of namespace Snmp_pp
#endif 

#endif // _SNMPv3