// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017 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 <mobius/crypt/hash_adler32.h>
#include <mobius/crypt/hash_crc32.h>
#include <mobius/crypt/hash_md5.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/unittest.h>
#include <cstring>

template <typename T> const std::string
get_hex_digest (T& h)
{
  return h.get_digest ().to_hexstring ();
}

template <typename T> void
update (T& h, const char *s)
{
  h.update (s, s + strlen (s));
}

static void
testcase_cipher_rc4 ()
{
  mobius::unittest test ("mobius::crypt::cipher_rc4");

  // key="Key", Plaintext="Plaintext"
  mobius::crypt::cipher_rc4 rc4_1 ("Key");
  mobius::bytearray b_1 = "Plaintext";

  test.ASSERT_EQUAL (b_1.to_hexstring (), "506c61696e74657874");
  rc4_1.encrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1.to_hexstring (), "bbf316e8d940af0ad3");
  rc4_1.reset ();
  rc4_1.decrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1.to_hexstring (), "506c61696e74657874");

  // key="Secret", Plaintext="Attack at dawn"
  mobius::crypt::cipher_rc4 rc4_2 ("Secret");
  mobius::bytearray b_2 = "Attack at dawn";

  test.ASSERT_EQUAL (b_2.to_hexstring (), "41747461636b206174206461776e");
  rc4_2.encrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2.to_hexstring (), "45a01f645fc35b383552544b9bf5");
  rc4_2.reset ();
  rc4_2.decrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2.to_hexstring (), "41747461636b206174206461776e");
  test.end ();
}

static void
testcase_cipher_zip ()
{
  mobius::unittest test ("mobius::crypt::cipher_zip");

  // key="Key", Plaintext="Plaintext"
  mobius::crypt::cipher_zip zip_1 ("Key");
  mobius::bytearray b_1 = "Plaintext";

  test.ASSERT_EQUAL (b_1.to_hexstring (), "506c61696e74657874");
  zip_1.encrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1.to_hexstring (), "fe1995e4fe54a8c6f3");
  zip_1.reset ();
  zip_1.decrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1.to_hexstring (), "506c61696e74657874");

  // key="Secret", Plaintext="Attack at dawn"
  mobius::crypt::cipher_zip zip_2 ("Secret");
  mobius::bytearray b_2 = "Attack at dawn";

  test.ASSERT_EQUAL (b_2.to_hexstring (), "41747461636b206174206461776e");
  zip_2.encrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2.to_hexstring (), "7595da02f5ec5c2c78755fd4069e");
  zip_2.reset ();
  zip_2.decrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2.to_hexstring (), "41747461636b206174206461776e");
  test.end ();
}

static void
testcase_cipher_des ()
{
  // DES - mode ECB
  mobius::crypt::cipher_des des0 ({0x00, 0xe8, 0x03, 0x00, 0x00, 0xe8, 0x03});
  mobius::bytearray b0 = {0x33, 0xb8, 0x17, 0xb0, 0x9b, 0x82, 0xa4, 0xf1};

  mobius::unittest test ("mobius::crypt::cipher_des");
  test.ASSERT_EQUAL (b0.to_hexstring (), "33b817b09b82a4f1");

  des0.decrypt (b0.begin (), b0.end (), b0.begin ());
  test.ASSERT_EQUAL (b0.to_hexstring (), "f4ed3a422ae3a1a6");

  // DES - mode ECB
  mobius::crypt::cipher_des des1 ({0x13, 0x34, 0x57, 0x79, 0x9b, 0xbc, 0xdf, 0xf1});
  mobius::bytearray b1 = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};

  test.ASSERT_EQUAL (b1.to_hexstring (), "0123456789abcdef");

  des1.encrypt (b1.begin (), b1.end (), b1.begin ());
  test.ASSERT_EQUAL (b1.to_hexstring (), "85e813540f0ab405");

  des1.decrypt (b1.begin (), b1.end (), b1.begin ());
  test.ASSERT_EQUAL (b1.to_hexstring (), "0123456789abcdef");

  // DES - mode ECB
  mobius::crypt::cipher_des des2 ({0x0e, 0x32, 0x92, 0x32, 0xea, 0x6d, 0x0d, 0x73});
  mobius::bytearray b2 (8);
  b2.fill (0x87);

  test.ASSERT_EQUAL (b2.to_hexstring (), "8787878787878787");

  des2.encrypt (b2.begin (), b2.end (), b2.begin ());
  test.ASSERT_EQUAL (b2.to_hexstring (), "0000000000000000");

  des2.decrypt (b2.begin (), b2.end (), b2.begin ());
  test.ASSERT_EQUAL (b2.to_hexstring (), "8787878787878787");

  // LM password "ABCD123"
  mobius::crypt::cipher_des des3 ("ABCD123");
  mobius::bytearray b3 ("KGS!@#$%");

  test.ASSERT_EQUAL (b3.to_hexstring (), "4b47532140232425");

  des3.encrypt (b3.begin (), b3.end (), b3.begin ());
  test.ASSERT_EQUAL (b3.to_hexstring (), "6f87cd328120cc55");

  des3.decrypt (b3.begin (), b3.end (), b3.begin ());
  test.ASSERT_EQUAL (b3.to_hexstring (), "4b47532140232425");

  // LM password "ABCDE12"
  mobius::crypt::cipher_des des4 ("ABCDE12");
  mobius::bytearray b4 ("KGS!@#$%");

  test.ASSERT_EQUAL (b4.to_hexstring (), "4b47532140232425");

  des4.encrypt (b4.begin (), b4.end (), b4.begin ());
  test.ASSERT_EQUAL (b4.to_hexstring (), "722ac01404a75156");

  des4.decrypt (b4.begin (), b4.end (), b4.begin ());
  test.ASSERT_EQUAL (b4.to_hexstring (), "4b47532140232425");

  // DES - mode CBC
  mobius::crypt::cipher_des des5 ("ABCDE12", new mobius::crypt::cipher_block_mode_cbc ({0, 0, 0, 0, 0, 0, 0, 0}));
  mobius::bytearray b5 ("KGS!@#$%");

  test.ASSERT_EQUAL (b5.to_hexstring (), "4b47532140232425");

  des5.encrypt (b5.begin (), b5.end (), b5.begin ());
  test.ASSERT_EQUAL (b5.to_hexstring (), "722ac01404a75156");

  des5.reset ();
  des5.decrypt (b5.begin (), b5.end (), b5.begin ());
  test.ASSERT_EQUAL (b5.to_hexstring (), "4b47532140232425");

  // DES - mode ECB
  mobius::crypt::cipher_des des6 ({0xe7, 0x37, 0x4e, 0x45, 0x8d, 0x54, 0xfd});
  mobius::bytearray b6 = {0x75, 0x3f, 0x61, 0x59, 0xc3, 0xa3, 0x11, 0x49};

  test.ASSERT_EQUAL (b6.to_hexstring (), "753f6159c3a31149");

  des6.decrypt (b6.begin (), b6.end (), b6.begin ());
  test.ASSERT_EQUAL (b6.to_hexstring (), "0000000001000000");

  // DES - mode CBC
  mobius::crypt::cipher_des des7 ({0x17,0xc0,0xfc,0x69,0x31,0x10,0xcb,0xd7}, new mobius::crypt::cipher_block_mode_cbc ({0, 0, 0, 0, 0, 0, 0, 0}));
  mobius::bytearray b7 ({0x62 ,0x75 ,0x32 ,0xeb ,0xd7 ,0x97 ,0xe0 ,0xfe ,0x7d ,0xf4 ,0x7c ,0x02 ,0xa5 ,0x12 ,0x61 ,0x3e ,0x9f ,0xdb ,0xd3 ,0x7e ,0x8b ,0x89 ,0x2d ,0xc1});

  test.ASSERT_EQUAL (b7.to_hexstring (), "627532ebd797e0fe7df47c02a512613e9fdbd37e8b892dc1");

  des7.decrypt (b7.begin (), b7.end (), b7.begin ());
  test.ASSERT_EQUAL (b7.to_hexstring (), "e5a3ac4e83fa5862f59f5773981c70030808080808080808");

  test.end ();
}

static void
testcase_hash_adler32 ()
{
  mobius::unittest test ("mobius::crypt::hash_adler32");

  mobius::crypt::hash_adler32 h1;
  mobius::crypt::hash_adler32 h2;
  mobius::crypt::hash_adler32 h3;

  test.ASSERT_EQUAL (get_hex_digest (h1), "00000001");
  test.ASSERT_EQUAL (get_hex_digest (h2), "00000001");
  test.ASSERT_EQUAL (get_hex_digest (h3), "00000001");

  // update
  update (h2, "abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "024d0127");

  update (h2, "d");
  test.ASSERT_EQUAL (get_hex_digest (h2), "03d8018b");

  update (h3, "abcd");
  test.ASSERT_EQUAL (get_hex_digest (h3), get_hex_digest (h2));

  test.end ();
}

static void
testcase_hash_crc32 ()
{
  mobius::unittest test ("mobius::crypt::hash_crc32");

  mobius::crypt::hash_crc32 h1;
  mobius::crypt::hash_crc32 h2;
  mobius::crypt::hash_crc32 h3;

  test.ASSERT_EQUAL (get_hex_digest (h1), "00000000");
  test.ASSERT_EQUAL (get_hex_digest (h2), "00000000");
  test.ASSERT_EQUAL (get_hex_digest (h3), "00000000");

  // update
  update (h2, "abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "352441c2");

  update (h2, "d");
  test.ASSERT_EQUAL (get_hex_digest (h2), "ed82cd11");

  update (h3, "abcd");
  test.ASSERT_EQUAL (get_hex_digest (h3), get_hex_digest (h2));

  test.end ();
}

static void
testcase_hash_md5 ()
{
  mobius::unittest test ("mobius::crypt::hash_md5");

  // RFC 1321 - section A.5
  mobius::crypt::hash_md5 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "d41d8cd98f00b204e9800998ecf8427e");

  mobius::crypt::hash_md5 h2;
  update (h2, "a");
  test.ASSERT_EQUAL (get_hex_digest (h2), "0cc175b9c0f1b6a831c399e269772661");

  mobius::crypt::hash_md5 h3;
  update (h3, "abc");
  test.ASSERT_EQUAL (get_hex_digest (h3), "900150983cd24fb0d6963f7d28e17f72");

  mobius::crypt::hash_md5 h4;
  update (h4, "message digest");
  test.ASSERT_EQUAL (get_hex_digest (h4), "f96b697d7cb7938d525a2f31aaf161d0");

  mobius::crypt::hash_md5 h5;
  update (h5, "abcdefghijklmnopqrstuvwxyz");
  test.ASSERT_EQUAL (get_hex_digest (h5), "c3fcd3d76192e4007dfb496cca67e13b");
  
  mobius::crypt::hash_md5 h6;
  update (h6, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
  test.ASSERT_EQUAL (get_hex_digest (h6), "d174ab98d277d9f5a5611c2c9f419d9f");
  
  mobius::crypt::hash_md5 h7;
  update (h7, "12345678901234567890123456789012345678901234567890123456789012345678901234567890");
  test.ASSERT_EQUAL (get_hex_digest (h7), "57edf4a22be3c955ac49da2e2107b67a");
  
  test.end ();
}

static void
testcase_hash_zip ()
{
  mobius::unittest test ("mobius::crypt::hash_zip");

  mobius::crypt::hash_zip h1;
  mobius::crypt::hash_zip h2;
  mobius::crypt::hash_zip h3;

  test.ASSERT_EQUAL (get_hex_digest (h1), "123456782345678934567890");
  test.ASSERT_EQUAL (get_hex_digest (h2), "123456782345678934567890");
  test.ASSERT_EQUAL (get_hex_digest (h3), "123456782345678934567890");

  // update
  update (h2, "abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "5dd2af4d589d03b43cf5ffa4");

  update (h2, "d");
  test.ASSERT_EQUAL (get_hex_digest (h2), "42ef4ac38d167254428e6d93");

  update (h3, "abcd");
  test.ASSERT_EQUAL (get_hex_digest (h3), get_hex_digest (h2));

  test.end ();
}

void
unittest_crypt ()
{
  testcase_cipher_des ();
  testcase_cipher_rc4 ();
  testcase_cipher_zip ();
  testcase_hash_adler32 ();
  testcase_hash_crc32 ();
  testcase_hash_md5 ();
  testcase_hash_zip ();
}
