// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief  C++ API crypt module wrapper
//!\author Eduardo Aguiar
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <Python.h>
#include <mobius/crypt/hash_zip.h>
#include <mobius/crypt/cipher_rc4.h>
#include <mobius/crypt/cipher_zip.h>
#include <mobius/crypt/cipher_des.h>
#include <mobius/crypt/cipher_block_mode_cbc.h>
#include <mobius/bytearray.h>

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt module methods
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef crypt_methods[] =
{
  {NULL, NULL, 0, NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: data structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
typedef struct
{
  PyObject_HEAD
  mobius::crypt::hash_zip *obj;
} crypt_hash_zip_o;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void crypt_hash_zip_tp_dealloc (crypt_hash_zip_o *);
static PyObject *crypt_hash_zip_tp_new (PyTypeObject *, PyObject *, PyObject *);
static PyObject *crypt_hash_zip_get_digest (crypt_hash_zip_o *);
static PyObject *crypt_hash_zip_f_update (crypt_hash_zip_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: getters and setters structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyGetSetDef crypt_hash_zip_getsetters[] =
{
  {
    (char *) "digest",
    (getter) crypt_hash_zip_get_digest,
    (setter) 0,
    (char *) "hash digest", NULL
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef crypt_hash_zip_methods[] =
{
  {
    (char *) "update",
    (PyCFunction) crypt_hash_zip_f_update,
    METH_VARARGS,
    "update hash"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject crypt_hash_zip_t =
{
  PyObject_HEAD_INIT (0)
  0,                                        		// ob_size
  "crypt.hash_zip",                      		// tp_name
  sizeof (crypt_hash_zip_o),             		// tp_basicsize
  0,                                        		// tp_itemsize
  (destructor) crypt_hash_zip_tp_dealloc,		// tp_dealloc
  0,                                        		// tp_print
  0,                                        		// tp_getattr
  0,                                        		// tp_setattr
  0,                                        		// tp_compare
  0,                                        		// tp_repr
  0,                                        		// tp_as_number
  0,                                        		// tp_as_sequence
  0,                                        		// tp_as_mapping
  0,                                        		// tp_hash
  0,                                        		// tp_call
  0,                                        		// tp_str
  0,                                        		// tp_getattro
  0,                                        		// tp_setattro
  0,                                        		// tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 		// tp_flags
  "zip cryptographic hash function",        		// tp_doc
  0,                                        		// tp_traverse
  0,                                        		// tp_clear
  0,                                        		// tp_richcompare
  0,                                        		// tp_weaklistoffset
  0,                                        		// tp_iter
  0,                                        		// tp_iternext
  crypt_hash_zip_methods,                		// tp_methods
  0,                                        		// tp_members
  crypt_hash_zip_getsetters,             		// tp_getset
  0,                                        		// tp_base
  0,                                        		// tp_dict
  0,                                        		// tp_descr_get
  0,                                        		// tp_descr_set
  0,                                        		// tp_dictoffset
  0,                                        		// tp_init
  0,                                        		// tp_alloc
  crypt_hash_zip_tp_new                  		// tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: data structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
typedef struct
{
  PyObject_HEAD
  mobius::crypt::cipher_rc4 *obj;
} crypt_cipher_rc4_o;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void crypt_cipher_rc4_tp_dealloc (crypt_cipher_rc4_o *);
static PyObject *crypt_cipher_rc4_tp_new (PyTypeObject *, PyObject *, PyObject *);
static PyObject *crypt_cipher_rc4_f_reset (crypt_cipher_rc4_o *, PyObject *);
static PyObject *crypt_cipher_rc4_f_encrypt (crypt_cipher_rc4_o *, PyObject *);
static PyObject *crypt_cipher_rc4_f_decrypt (crypt_cipher_rc4_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef crypt_cipher_rc4_methods[] =
{
  {
    (char *) "reset",
    (PyCFunction) crypt_cipher_rc4_f_reset,
    METH_VARARGS,
    "reset encryption/decryption"
  },
  {
    (char *) "encrypt",
    (PyCFunction) crypt_cipher_rc4_f_encrypt,
    METH_VARARGS,
    "encrypt string"
  },
  {
    (char *) "decrypt",
    (PyCFunction) crypt_cipher_rc4_f_decrypt,
    METH_VARARGS,
    "decrypt string"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject crypt_cipher_rc4_t =
{
  PyObject_HEAD_INIT (0)
  0,                                                    // ob_size
  "crypt.cipher_rc4",                                   // tp_name
  sizeof (crypt_cipher_rc4_o),                          // tp_basicsize
  0,                                                    // tp_itemsize
  (destructor) crypt_cipher_rc4_tp_dealloc,             // tp_dealloc
  0,                                                    // tp_print
  0,                                                    // tp_getattr
  0,                                                    // tp_setattr
  0,                                                    // tp_compare
  0,                                                    // tp_repr
  0,                                                    // tp_as_number
  0,                                                    // tp_as_sequence
  0,                                                    // tp_as_mapping
  0,                                                    // tp_hash
  0,                                                    // tp_call
  0,                                                    // tp_str
  0,                                                    // tp_getattro
  0,                                                    // tp_setattro
  0,                                                    // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,             // tp_flags
  "alleged RC4 stream cipher algorithm",                // tp_doc
  0,                                                    // tp_traverse
  0,                                                    // tp_clear
  0,                                                    // tp_richcompare
  0,                                                    // tp_weaklistoffset
  0,                                                    // tp_iter
  0,                                                    // tp_iternext
  crypt_cipher_rc4_methods,                             // tp_methods
  0,                                                    // tp_members
  0,                                                    // tp_getset
  0,                                                    // tp_base
  0,                                                    // tp_dict
  0,                                                    // tp_descr_get
  0,                                                    // tp_descr_set
  0,                                                    // tp_dictoffset
  0,                                                    // tp_init
  0,                                                    // tp_alloc
  crypt_cipher_rc4_tp_new                               // tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: data structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
typedef struct
{
  PyObject_HEAD
  mobius::crypt::cipher_zip *obj;
} crypt_cipher_zip_o;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void crypt_cipher_zip_tp_dealloc (crypt_cipher_zip_o *);
static PyObject *crypt_cipher_zip_tp_new (PyTypeObject *, PyObject *, PyObject *);
static PyObject *crypt_cipher_zip_f_reset (crypt_cipher_zip_o *, PyObject *);
static PyObject *crypt_cipher_zip_f_encrypt (crypt_cipher_zip_o *, PyObject *);
static PyObject *crypt_cipher_zip_f_decrypt (crypt_cipher_zip_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef crypt_cipher_zip_methods[] =
{
  {
    (char *) "reset",
    (PyCFunction) crypt_cipher_zip_f_reset,
    METH_VARARGS,
    "reset encryption/decryption"
  },
  {
    (char *) "encrypt",
    (PyCFunction) crypt_cipher_zip_f_encrypt,
    METH_VARARGS,
    "encrypt string"
  },
  {
    (char *) "decrypt",
    (PyCFunction) crypt_cipher_zip_f_decrypt,
    METH_VARARGS,
    "decrypt string"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject crypt_cipher_zip_t =
{
  PyObject_HEAD_INIT (0)
  0,                                                    // ob_size
  "crypt.cipher_zip",                                   // tp_name
  sizeof (crypt_cipher_zip_o),                          // tp_basicsize
  0,                                                    // tp_itemsize
  (destructor) crypt_cipher_zip_tp_dealloc,             // tp_dealloc
  0,                                                    // tp_print
  0,                                                    // tp_getattr
  0,                                                    // tp_setattr
  0,                                                    // tp_compare
  0,                                                    // tp_repr
  0,                                                    // tp_as_number
  0,                                                    // tp_as_sequence
  0,                                                    // tp_as_mapping
  0,                                                    // tp_hash
  0,                                                    // tp_call
  0,                                                    // tp_str
  0,                                                    // tp_getattro
  0,                                                    // tp_setattro
  0,                                                    // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,             // tp_flags
  "zip stream cipher algorithm",                        // tp_doc
  0,                                                    // tp_traverse
  0,                                                    // tp_clear
  0,                                                    // tp_richcompare
  0,                                                    // tp_weaklistoffset
  0,                                                    // tp_iter
  0,                                                    // tp_iternext
  crypt_cipher_zip_methods,                             // tp_methods
  0,                                                    // tp_members
  0,                                                    // tp_getset
  0,                                                    // tp_base
  0,                                                    // tp_dict
  0,                                                    // tp_descr_get
  0,                                                    // tp_descr_set
  0,                                                    // tp_dictoffset
  0,                                                    // tp_init
  0,                                                    // tp_alloc
  crypt_cipher_zip_tp_new                               // tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: data structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
typedef struct
{
  PyObject_HEAD
  mobius::crypt::cipher_des *obj;
} crypt_cipher_des_o;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void crypt_cipher_des_tp_dealloc (crypt_cipher_des_o *);
static PyObject *crypt_cipher_des_tp_new (PyTypeObject *, PyObject *, PyObject *);
static PyObject *crypt_cipher_des_f_reset (crypt_cipher_des_o *, PyObject *);
static PyObject *crypt_cipher_des_f_encrypt (crypt_cipher_des_o *, PyObject *);
static PyObject *crypt_cipher_des_f_decrypt (crypt_cipher_des_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef crypt_cipher_des_methods[] =
{
  {
    (char *) "reset",
    (PyCFunction) crypt_cipher_des_f_reset,
    METH_VARARGS,
    "reset encryption/decryption"
  },
  {
    (char *) "encrypt",
    (PyCFunction) crypt_cipher_des_f_encrypt,
    METH_VARARGS,
    "encrypt string"
  },
  {
    (char *) "decrypt",
    (PyCFunction) crypt_cipher_des_f_decrypt,
    METH_VARARGS,
    "decrypt string"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject crypt_cipher_des_t =
{
  PyObject_HEAD_INIT (0)
  0,                                                    // ob_size
  "crypt.cipher_des",                                   // tp_name
  sizeof (crypt_cipher_des_o),                          // tp_basicsize
  0,                                                    // tp_itemsize
  (destructor) crypt_cipher_des_tp_dealloc,             // tp_dealloc
  0,                                                    // tp_print
  0,                                                    // tp_getattr
  0,                                                    // tp_setattr
  0,                                                    // tp_compare
  0,                                                    // tp_repr
  0,                                                    // tp_as_number
  0,                                                    // tp_as_sequence
  0,                                                    // tp_as_mapping
  0,                                                    // tp_hash
  0,                                                    // tp_call
  0,                                                    // tp_str
  0,                                                    // tp_getattro
  0,                                                    // tp_setattro
  0,                                                    // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,             // tp_flags
  "DES block cipher algorithm",                         // tp_doc
  0,                                                    // tp_traverse
  0,                                                    // tp_clear
  0,                                                    // tp_richcompare
  0,                                                    // tp_weaklistoffset
  0,                                                    // tp_iter
  0,                                                    // tp_iternext
  crypt_cipher_des_methods,                             // tp_methods
  0,                                                    // tp_members
  0,                                                    // tp_getset
  0,                                                    // tp_base
  0,                                                    // tp_dict
  0,                                                    // tp_descr_get
  0,                                                    // tp_descr_set
  0,                                                    // tp_dictoffset
  0,                                                    // tp_init
  0,                                                    // tp_alloc
  crypt_cipher_des_tp_new                               // tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: tp_new
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_hash_zip_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  crypt_hash_zip_o *self = (crypt_hash_zip_o *) type->tp_alloc (type, 0);
  if (self != NULL)
    self->obj = new mobius::crypt::hash_zip ();

  return (PyObject *) self;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
crypt_hash_zip_tp_dealloc (crypt_hash_zip_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: digest getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_hash_zip_get_digest (crypt_hash_zip_o *self)
{
  mobius::bytearray array = self->obj->get_digest ();

  return PyString_FromStringAndSize ((const char *) array.data (), array.size ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.hash_zip: update
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_hash_zip_f_update (crypt_hash_zip_o *self, PyObject *args)
{
  // parse input args
  const char *arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  self->obj->update (arg_data_buffer, arg_data_buffer + arg_data_size);

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: tp_new
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_rc4_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  // parse input args
  const char *arg_key_buffer;
  int arg_key_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_key_buffer, &arg_key_size))
    return NULL;

  crypt_cipher_rc4_o *self = (crypt_cipher_rc4_o *) type->tp_alloc (type, 0);
  if (self != NULL)
    {
      mobius::bytearray key ((unsigned char *) arg_key_buffer, arg_key_size);
      self->obj = new mobius::crypt::cipher_rc4 (key);
    }

  return (PyObject *) self;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
crypt_cipher_rc4_tp_dealloc (crypt_cipher_rc4_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: reset
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_rc4_f_reset (crypt_cipher_rc4_o *self, PyObject *args)
{
  // call C++ function
  self->obj->reset ();

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: encrypt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_rc4_f_encrypt (crypt_cipher_rc4_o *self, PyObject *args)
{
  // parse input args
  const char * arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // call C++ code
  char out[arg_data_size];
  self->obj->encrypt (arg_data_buffer, arg_data_buffer + arg_data_size, out);

  // return value
  return PyString_FromStringAndSize (out, arg_data_size);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_rc4: decrypt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_rc4_f_decrypt (crypt_cipher_rc4_o *self, PyObject *args)
{
  // parse input args
  const char * arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // call C++ code
  char out[arg_data_size];
  self->obj->decrypt (arg_data_buffer, arg_data_buffer + arg_data_size, out);

  // return value
  return PyString_FromStringAndSize (out, arg_data_size);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: tp_new
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_zip_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  // parse input args
  const char *arg_key_buffer;
  int arg_key_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_key_buffer, &arg_key_size))
    return NULL;

  crypt_cipher_zip_o *self = (crypt_cipher_zip_o *) type->tp_alloc (type, 0);
  if (self != NULL)
    {
      mobius::bytearray key ((unsigned char *) arg_key_buffer, arg_key_size);
      self->obj = new mobius::crypt::cipher_zip (key);
    }

  return (PyObject *) self;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
crypt_cipher_zip_tp_dealloc (crypt_cipher_zip_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: reset
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_zip_f_reset (crypt_cipher_zip_o *self, PyObject *args)
{
  // call C++ function
  self->obj->reset ();

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: encrypt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_zip_f_encrypt (crypt_cipher_zip_o *self, PyObject *args)
{
  // parse input args
  const char * arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // call C++ code
  char out[arg_data_size];
  self->obj->encrypt (arg_data_buffer, arg_data_buffer + arg_data_size, out);

  // return value
  return PyString_FromStringAndSize (out, arg_data_size);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_zip: decrypt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_zip_f_decrypt (crypt_cipher_zip_o *self, PyObject *args)
{
  // parse input args
  const char * arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // call C++ code
  char out[arg_data_size];
  self->obj->decrypt (arg_data_buffer, arg_data_buffer + arg_data_size, out);

  // return value
  return PyString_FromStringAndSize (out, arg_data_size);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: tp_new
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_des_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  // parse input args
  const char *arg_key_buffer = nullptr;
  int arg_key_size = 0;
  const char *arg_mode = nullptr;
  const char *arg_iv = nullptr;
  int arg_iv_size = 0;

  if (!PyArg_ParseTuple (args, "s#|ss#", &arg_key_buffer, &arg_key_size, &arg_mode, &arg_iv, &arg_iv_size))
    return NULL;

  if (arg_key_size != 7 && arg_key_size != 8)
    {
      PyErr_SetString (PyExc_ValueError, "key size must be either 7 or 8 bytes");
      return NULL;
    }

  if (arg_mode && strcmp (arg_mode, "cbc") != 0)
    {
      PyErr_SetString (PyExc_ValueError, "invalid DES mode");
      return NULL;
    }

  // create object
  crypt_cipher_des_o *self = (crypt_cipher_des_o *) type->tp_alloc (type, 0);
  if (self != NULL)
    {
      mobius::bytearray key ((unsigned char *) arg_key_buffer, arg_key_size);

      if (arg_mode)
        {
          mobius::bytearray iv = {0, 0, 0, 0, 0, 0, 0, 0};

          if (arg_iv != nullptr)
            iv = mobius::bytearray ((unsigned char *) arg_iv, arg_iv_size);

          self->obj = new mobius::crypt::cipher_des (key, new mobius::crypt::cipher_block_mode_cbc (iv));
        }

      else
        self->obj = new mobius::crypt::cipher_des (key);
    }

  return (PyObject *) self;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
crypt_cipher_des_tp_dealloc (crypt_cipher_des_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: reset
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_des_f_reset (crypt_cipher_des_o *self, PyObject *args)
{
  // call C++ function
  self->obj->reset ();

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: encrypt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_des_f_encrypt (crypt_cipher_des_o *self, PyObject *args)
{
  // parse input args
  const char * arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // call C++ code
  char out[arg_data_size];
  self->obj->encrypt (arg_data_buffer, arg_data_buffer + arg_data_size, out);

  // return value
  return PyString_FromStringAndSize (out, arg_data_size);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt.cipher_des: decrypt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
crypt_cipher_des_f_decrypt (crypt_cipher_des_o *self, PyObject *args)
{
  // parse input args
  const char * arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // call C++ code
  char out[arg_data_size];
  self->obj->decrypt (arg_data_buffer, arg_data_buffer + arg_data_size, out);
  
  // return value
  return PyString_FromStringAndSize (out, arg_data_size);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief crypt module initialisation function
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyMODINIT_FUNC
initcrypt ()
{
  if (PyType_Ready (&crypt_hash_zip_t) < 0) return;
  if (PyType_Ready (&crypt_cipher_rc4_t) < 0) return;
  if (PyType_Ready (&crypt_cipher_zip_t) < 0) return;
  if (PyType_Ready (&crypt_cipher_des_t) < 0) return;

  PyObject* m = Py_InitModule3 ("crypt", crypt_methods, "Mobius Forensic Toolkit API wrapper");
  if (m == NULL)
    return;

  Py_INCREF (&crypt_hash_zip_t);
  PyModule_AddObject (m, "hash_zip", (PyObject *) &crypt_hash_zip_t);
  Py_INCREF (&crypt_cipher_rc4_t);
  PyModule_AddObject (m, "cipher_rc4", (PyObject *) &crypt_cipher_rc4_t);
  Py_INCREF (&crypt_cipher_zip_t);
  PyModule_AddObject (m, "cipher_zip", (PyObject *) &crypt_cipher_zip_t);
  Py_INCREF (&crypt_cipher_des_t);
  PyModule_AddObject (m, "cipher_des", (PyObject *) &crypt_cipher_des_t);
}