// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024 Eduardo Aguiar
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "cipher.h"
#include "cipher_impl_null.h"
#include "gcrypt/cipher_impl.h"
#include "cipher_impl_ige.h"
#include "cipher_impl_zip.h"

namespace mobius::crypt
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Default constructor
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher::cipher ()
  : impl_ (std::make_shared <cipher_impl_null> ())
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Constructor from implementation pointer
//! \param impl Implementation pointer
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher::cipher (const std::shared_ptr <cipher_impl_base>& impl)
  : impl_ (impl)
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with CBC mode
//! \param id Cipher ID
//! \param key Cipher key
//! \param iv Initialization vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_cbc (const std::string& id, const bytearray& key, const bytearray& iv)
{
  auto c = std::make_shared <gcrypt::cipher_impl> (id, "cbc");
  c->set_key (key);

  if (iv)
    c->set_iv (iv);
  else
    c->set_iv (mobius::bytearray (c->get_block_size ()));

  return cipher (c);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with CBC/CTS mode
//! \param id Cipher ID
//! \param key Cipher key
//! \param iv Initialization vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_cbc_cts (const std::string& id, const bytearray& key, const bytearray& iv)
{
  auto c = std::make_shared <gcrypt::cipher_impl> (id, "cbc-cts");
  c->set_key (key);

  if (iv)
    c->set_iv (iv);
  else
    c->set_iv (mobius::bytearray (c->get_block_size ()));

  return cipher (c);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with CTR mode
//! \param id Cipher ID
//! \param key Cipher key
//! \param cv Counter vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_ctr (const std::string& id, const bytearray& key, const bytearray& cv)
{
  auto c = std::make_shared <gcrypt::cipher_impl> (id, "ctr");
  c->set_key (key);

  if (cv)
    c->set_counter (cv);

  else
    {
      mobius::bytearray counter (c->get_block_size ());
      counter[counter.size () - 1] = 1;
      c->set_counter (counter);
    }

  return cipher (c);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with ECB mode
//! \param id Cipher ID
//! \param key Cipher key
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_ecb (const std::string& id, const bytearray& key)
{
  auto c = std::make_shared <gcrypt::cipher_impl> (id, "ecb");
  c->set_key (key);

  return cipher (c);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with GCM mode
//! \param id Cipher ID
//! \param key Cipher key
//! \param iv Initialization vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_gcm (const std::string& id, const bytearray& key, const bytearray& iv)
{
  auto c = std::make_shared <gcrypt::cipher_impl> (id, "gcm");
  c->set_key (key);

  if (iv)
    c->set_iv (iv);
  else
    c->set_iv (mobius::bytearray (c->get_block_size ()));

  return cipher (c);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with IGE mode
//! \param id Cipher ID
//! \param key Cipher key
//! \param iv Initialization vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_ige (const std::string& id, const bytearray& key, const bytearray& iv)
{
  return cipher (std::make_shared <cipher_impl_ige> (id, key, iv));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new cipher object with OFB mode
//! \param id Cipher ID
//! \param key Cipher key
//! \param iv Initialization vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_ofb (const std::string& id, const bytearray& key, const bytearray& iv)
{
  auto c = std::make_shared <gcrypt::cipher_impl> (id, "ofb");
  c->set_key (key);

  if (iv)
    c->set_iv (iv);
  else
    c->set_iv (mobius::bytearray (c->get_block_size ()));

  return cipher (c);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new stream cipher object
//! \param id Cipher ID
//! \param key Cipher key
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher
new_cipher_stream (const std::string& id, const bytearray& key)
{
  if (id == "zip")
    return cipher (std::make_shared <cipher_impl_zip> (key));

  else
    {
      auto c = std::make_shared <gcrypt::cipher_impl> (id, "stream");
      c->set_key (key);
      return cipher (c);
    }
}

} // namespace mobius::crypt
