// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 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_3des.h"
#include <mobius/exception.inc>
#include <stdexcept>

namespace mobius
{
namespace crypt
{
namespace
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get key1 from 3DES key
//! \param key 3DES key
//! \return key1
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static mobius::bytearray
get_key1 (const mobius::bytearray& key)
{
  if (key.size () == 21)
    return key.slice (0, 6);

  else if (key.size () == 24)
    return key.slice (0, 7);

  else
    throw std::out_of_range (MOBIUS_EXCEPTION_MSG ("key size must be either 21 or 24 bytes"));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get key2 from 3DES key
//! \param key 3DES key
//! \return key2
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static mobius::bytearray
get_key2 (const mobius::bytearray& key)
{
  if (key.size () == 21)
    return key.slice (7, 13);

  else if (key.size () == 24)
    return key.slice (8, 15);

  else
    throw std::out_of_range (MOBIUS_EXCEPTION_MSG ("key size must be either 21 or 24 bytes"));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get key3 from 3DES key
//! \param key 3DES key
//! \return key3
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static mobius::bytearray
get_key3 (const mobius::bytearray& key)
{
  if (key.size () == 21)
    return key.slice (14, 20);

  else if (key.size () == 24)
    return key.slice (16, 23);

  else
    throw std::out_of_range (MOBIUS_EXCEPTION_MSG ("key size must be either 21 or 24 bytes"));
}

} // namespace

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
//! \param key encryption/decryption key
//! \param mode cipher block mode
//! \param iv initialization vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
cipher_3des::cipher_3des (
  const mobius::bytearray& key,
  const std::string& mode,
  const mobius::bytearray& iv
)
  : cipher_block (8, mode, iv),
    des_1_ (get_key1 (key), mode, iv),
    des_2_ (get_key2 (key), mode, iv),
    des_3_ (get_key3 (key), mode, iv)
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief encrypt data block
//! \param b data block
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
cipher_3des::encrypt_block (mobius::bytearray& b) noexcept
{
  des_1_.encrypt (b.begin (), b.end (), b.begin ());
  des_2_.decrypt (b.begin (), b.end (), b.begin ());
  des_3_.encrypt (b.begin (), b.end (), b.begin ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief decrypt data block
//! \param b data block
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
cipher_3des::decrypt_block (mobius::bytearray& b) noexcept
{
  des_3_.decrypt (b.begin (), b.end (), b.begin ());
  des_2_.encrypt (b.begin (), b.end (), b.begin ());
  des_1_.decrypt (b.begin (), b.end (), b.begin ());
}


} // namespace crypt
} // namespace mobius
