// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 <mobius/crypt/cipher_aes.h>
#include <mobius/crypt/cipher_rc4.h>
#include <mobius/crypt/cipher_rot13.h>
#include <mobius/crypt/cipher_zip.h>
#include <mobius/crypt/cipher_des.h>
#include <mobius/crypt/cipher_block_mode.h>
#include <mobius/crypt/hash_adler32.h>
#include <mobius/crypt/hash_crc32.h>
#include <mobius/crypt/hash_md5.h>
#include <mobius/crypt/hash_sha1.h>
#include <mobius/crypt/hash_sha2_224.h>
#include <mobius/crypt/hash_sha2_256.h>
#include <mobius/crypt/hash_sha2_384.h>
#include <mobius/crypt/hash_sha2_512.h>
#include <mobius/crypt/hash_sha2_512_224.h>
#include <mobius/crypt/hash_sha2_512_256.h>
#include <mobius/crypt/hash_zip.h>
#include <mobius/unittest.h>
#include <cstring>

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

static void
testcase_cipher_aes ()
{
  mobius::unittest test ("mobius::crypt::cipher_aes");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_ModesA_All.pdf

  const mobius::bytearray b_plaintext =
  {
    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
    0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
    0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
    0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
  };

  const mobius::bytearray iv =
  {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
  };

  mobius::bytearray b (b_plaintext.size ());

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // AES128 - mode ECB
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray b1_ciphertext =
  {
    0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,
    0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf,
    0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88,
    0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4
  };

  mobius::crypt::cipher_aes aes1 ({0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c});

  b = aes1.encrypt (b_plaintext);
  test.ASSERT_EQUAL (b, b1_ciphertext);

  b = aes1.decrypt (b1_ciphertext);
  test.ASSERT_EQUAL (b, b_plaintext);
  
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // AES192 - mode ECB
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray b2_ciphertext =
  {
    0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc,
    0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef,
    0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e,
    0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e  
  };

  mobius::crypt::cipher_aes aes2 ({0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b});

  b = aes2.encrypt (b_plaintext);
  test.ASSERT_EQUAL (b, b2_ciphertext);

  b = aes2.decrypt (b2_ciphertext);
  test.ASSERT_EQUAL (b, b_plaintext);

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // AES256 - mode ECB
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray b3_ciphertext =
  {
    0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8,
    0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70,
    0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d,
    0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7
  };

  mobius::crypt::cipher_aes aes3 ({0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4});

  b = aes3.encrypt (b_plaintext);
  test.ASSERT_EQUAL (b, b3_ciphertext);

  b = aes3.decrypt (b3_ciphertext);
  test.ASSERT_EQUAL (b, b_plaintext);

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // AES128 - mode CBC
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray b4_ciphertext =
  {
    0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
    0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
    0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
    0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7
  };

  mobius::crypt::cipher_aes aes4a ({0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, mobius::crypt::new_cipher_block_mode_cbc (iv));

  b = aes4a.encrypt (b_plaintext);
  test.ASSERT_EQUAL (b, b4_ciphertext);

  mobius::crypt::cipher_aes aes4b ({0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, mobius::crypt::new_cipher_block_mode_cbc (iv));

  b = aes4b.decrypt (b4_ciphertext);
  test.ASSERT_EQUAL (b, b_plaintext);

  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");

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

  b1 = des1.decrypt (b1);
  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");

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

  b2 = des2.decrypt (b2);
  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");

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

  b3 = des3.decrypt (b3);
  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");

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

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

  // DES - mode CBC
  mobius::crypt::cipher_des des5 ("ABCDE12", mobius::crypt::new_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}, mobius::crypt::new_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_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_rot13 ()
{
  mobius::unittest test ("mobius::crypt::cipher_rot13");

  // nowhere <-> abjurer
  mobius::crypt::cipher_rot13 c1;
  mobius::bytearray b_1 = "nowhere";

  c1.encrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1, "abjurer");
  c1.decrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1, "nowhere");

  // encrypt + encrypt = encrypt + decrypt
  c1.encrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1, "abjurer");
  c1.encrypt (b_1.begin (), b_1.end (), b_1.begin ());
  test.ASSERT_EQUAL (b_1, "nowhere");

  // NERF <-> ARES
  mobius::crypt::cipher_rot13 c2;
  mobius::bytearray b_2 = "NERF";

  c2.encrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2, "ARES");
  c2.decrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2, "NERF");

  // encrypt + encrypt = encrypt + decrypt
  c2.encrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2, "ARES");
  c2.encrypt (b_2.begin (), b_2.end (), b_2.begin ());
  test.ASSERT_EQUAL (b_2, "NERF");

  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_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
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "024d0127");

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

  h3.update ("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
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "352441c2");

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

  h3.update ("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;
  h2.update ("a");
  test.ASSERT_EQUAL (get_hex_digest (h2), "0cc175b9c0f1b6a831c399e269772661");

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

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

  mobius::crypt::hash_md5 h5;
  h5.update ("abcdefghijklmnopqrstuvwxyz");
  test.ASSERT_EQUAL (get_hex_digest (h5), "c3fcd3d76192e4007dfb496cca67e13b");
  
  mobius::crypt::hash_md5 h6;
  h6.update ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
  test.ASSERT_EQUAL (get_hex_digest (h6), "d174ab98d277d9f5a5611c2c9f419d9f");
  
  mobius::crypt::hash_md5 h7;
  h7.update ("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
  test.ASSERT_EQUAL (get_hex_digest (h7), "57edf4a22be3c955ac49da2e2107b67a");
  
  mobius::crypt::hash_md5 h8;
  mobius::bytearray b1 (1000);
  b1.fill (0);
  h8.update (b1);
  test.ASSERT_EQUAL (get_hex_digest (h8), "ede3d3b685b4e137ba4cb2521329a75e");

  test.end ();
}

static void
testcase_hash_sha1 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha1");

  // RFC 3174 - section 7.3
  mobius::crypt::hash_sha1 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "da39a3ee5e6b4b0d3255bfef95601890afd80709");

  mobius::crypt::hash_sha1 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "a9993e364706816aba3e25717850c26c9cd0d89d");

  mobius::crypt::hash_sha1 h3;
  h3.update ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
  test.ASSERT_EQUAL (get_hex_digest (h3), "84983e441c3bd26ebaae4aa1f95129e5e54670f1");

  mobius::crypt::hash_sha1 h4;

  for (int i = 0;i < 1000000;i++)
    h4.update ("a");

  test.ASSERT_EQUAL (get_hex_digest (h4), "34aa973cd4c4daa4f61eeb2bdbad27316534016f");

  mobius::crypt::hash_sha1 h5;
  
  for (int i = 0;i < 20;i++)
    h5.update ("01234567012345670123456701234567");

  test.ASSERT_EQUAL (get_hex_digest (h5), "dea356a2cddd90c7a7ecedc5ebb563934f460452");
  
  test.end ();
}

static void
testcase_hash_sha2_224 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha2_224");

  mobius::crypt::hash_sha2_224 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA224.pdf
  mobius::crypt::hash_sha2_224 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7");

  mobius::crypt::hash_sha2_224 h3;
  h3.update ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
  test.ASSERT_EQUAL (get_hex_digest (h3), "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525");
  
  test.end ();
}

static void
testcase_hash_sha2_256 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha2_256");

  mobius::crypt::hash_sha2_256 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf
  mobius::crypt::hash_sha2_256 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");

  mobius::crypt::hash_sha2_256 h3;
  h3.update ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
  test.ASSERT_EQUAL (get_hex_digest (h3), "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
  
  test.end ();
}

static void
testcase_hash_sha2_384 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha2_384");

  mobius::crypt::hash_sha2_384 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA384.pdf
  mobius::crypt::hash_sha2_384 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7");

  mobius::crypt::hash_sha2_384 h3;
  h3.update ( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
  test.ASSERT_EQUAL (get_hex_digest (h3), "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039");
  
  test.end ();
}

static void
testcase_hash_sha2_512 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha2_512");

  mobius::crypt::hash_sha2_512 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512.pdf
  mobius::crypt::hash_sha2_512 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");

  mobius::crypt::hash_sha2_512 h3;
  h3.update ( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
  test.ASSERT_EQUAL (get_hex_digest (h3), "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909");
  
  test.end ();
}

static void
testcase_hash_sha2_512_224 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha2_512_224");

  mobius::crypt::hash_sha2_512_224 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512_224.pdf
  mobius::crypt::hash_sha2_512_224 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa");

  mobius::crypt::hash_sha2_512_224 h3;
  h3.update ( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
  test.ASSERT_EQUAL (get_hex_digest (h3), "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9");
  
  test.end ();
}

static void
testcase_hash_sha2_512_256 ()
{
  mobius::unittest test ("mobius::crypt::hash_sha2_512_256");

  mobius::crypt::hash_sha2_512_256 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a");

  //! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512_256.pdf
  mobius::crypt::hash_sha2_512_256 h2;
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23");

  mobius::crypt::hash_sha2_512_256 h3;
  h3.update ( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
  test.ASSERT_EQUAL (get_hex_digest (h3), "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a");
  
  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
  h2.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h2), "5dd2af4d589d03b43cf5ffa4");

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

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

  test.end ();
}

void
unittest_crypt ()
{
  testcase_cipher_aes ();
  testcase_cipher_des ();
  testcase_cipher_rc4 ();
  testcase_cipher_rot13 ();
  testcase_cipher_zip ();
  testcase_hash_adler32 ();
  testcase_hash_crc32 ();
  testcase_hash_md5 ();
  testcase_hash_sha1 ();
  testcase_hash_sha2_224 ();
  testcase_hash_sha2_256 ();
  testcase_hash_sha2_384 ();
  testcase_hash_sha2_512 ();
  testcase_hash_sha2_512_224 ();
  testcase_hash_sha2_512_256 ();
  testcase_hash_zip ();
}
