// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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_3des.h>
#include <mobius/crypt/cipher_aes.h>
#include <mobius/crypt/cipher_blowfish.h>
#include <mobius/crypt/cipher_des.h>
#include <mobius/crypt/cipher_rc4.h>
#include <mobius/crypt/cipher_rot13.h>
#include <mobius/crypt/cipher_zip.h>
#include <mobius/crypt/hash_adler32.h>
#include <mobius/crypt/hash_crc32.h>
#include <mobius/crypt/hash_md4.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/crypt/hmac.h>
#include <mobius/crypt/crypt.h>
#include <mobius/unittest.h>
#include <cstring>

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

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TDES_Core.pdf
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
testcase_cipher_3des ()
{
  mobius::unittest test ("mobius::crypt::cipher_3des");

  // 3DES - mode ECB
  mobius::crypt::cipher_3des des0 ({0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef});

  mobius::bytearray b0 = {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};

  mobius::bytearray b1 (b0);
  des0.encrypt (b1.begin (), b1.end (), b1.begin ());

  test.ASSERT_EQUAL (b1.to_hexstring (), "06ede3d82884090aff322c19f0518486730576972a666e58b6c88cf107340d3d");

  des0.decrypt (b1.begin (), b1.end (), b1.begin ());

  test.ASSERT_EQUAL (b0.to_hexstring (), "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");

  // 3DES - mode ECB
  mobius::crypt::cipher_3des des1 ({0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23});

  b1 = b0;
  
  des1.encrypt (b1.begin (), b1.end (), b1.begin ());

  test.ASSERT_EQUAL (b1.to_hexstring (), "714772f339841d34267fcc4bd2949cc3ee11c22a576a303876183f99c0b6de87");

  des1.decrypt (b1.begin (), b1.end (), b1.begin ());

  test.ASSERT_EQUAL (b0.to_hexstring (), "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");

  test.end ();
}

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}, "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}, "cbc", iv);

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

  test.end ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \see https://www.schneier.com/code/vectors.txt
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
testcase_cipher_blowfish ()
{
  mobius::unittest test ("mobius::crypt::cipher_blowfish");

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // mode ECB - key length = 64 bits
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray values1[34][3] =    // key, plaintext, ciphertext
  {
    {
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0x4e, 0xf9, 0x97, 0x45, 0x61, 0x98, 0xdd, 0x78}
    },
    {
       {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
       {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
       {0x51, 0x86, 0x6f, 0xd5, 0xb8, 0x5e, 0xcb, 0x8a}
    },
    {
       {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
       {0x7d, 0x85, 0x6f, 0x9a, 0x61, 0x30, 0x63, 0xf2}
    },
    {
       {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
       {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
       {0x24, 0x66, 0xdd, 0x87, 0x8b, 0x96, 0x3c, 0x9d}
    },
    {
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
       {0x61, 0xf9, 0xc3, 0x80, 0x22, 0x81, 0xb0, 0x96}
    },
    {
       {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0x7d, 0x0c, 0xc6, 0x30, 0xaf, 0xda, 0x1e, 0xc7}
    },
    {
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0x4e, 0xf9, 0x97, 0x45, 0x61, 0x98, 0xdd, 0x78}
    },
    {
       {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0x0a, 0xce, 0xab, 0x0f, 0xc6, 0xa0, 0xa2, 0x8d}
    },
    {
       {0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57},
       {0x01, 0xa1, 0xd6, 0xd0, 0x39, 0x77, 0x67, 0x42},
       {0x59, 0xc6, 0x82, 0x45, 0xeb, 0x05, 0x28, 0x2b}
    },
    {
       {0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e},
       {0x5c, 0xd5, 0x4c, 0xa8, 0x3d, 0xef, 0x57, 0xda},
       {0xb1, 0xb8, 0xcc, 0x0b, 0x25, 0x0f, 0x09, 0xa0}
    },
    {
       {0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86},
       {0x02, 0x48, 0xd4, 0x38, 0x06, 0xf6, 0x71, 0x72},
       {0x17, 0x30, 0xe5, 0x77, 0x8b, 0xea, 0x1d, 0xa4}
    },
    {
       {0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e},
       {0x51, 0x45, 0x4b, 0x58, 0x2d, 0xdf, 0x44, 0x0a},
       {0xa2, 0x5e, 0x78, 0x56, 0xcf, 0x26, 0x51, 0xeb}
    },
    {
       {0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6},
       {0x42, 0xfd, 0x44, 0x30, 0x59, 0x57, 0x7f, 0xa2},
       {0x35, 0x38, 0x82, 0xb1, 0x09, 0xce, 0x8f, 0x1a}
    },
    {
       {0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce},
       {0x05, 0x9b, 0x5e, 0x08, 0x51, 0xcf, 0x14, 0x3a},
       {0x48, 0xf4, 0xd0, 0x88, 0x4c, 0x37, 0x99, 0x18}
    },
    {
       {0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6},
       {0x07, 0x56, 0xd8, 0xe0, 0x77, 0x47, 0x61, 0xd2},
       {0x43, 0x21, 0x93, 0xb7, 0x89, 0x51, 0xfc, 0x98}
    },
    {
       {0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe},
       {0x76, 0x25, 0x14, 0xb8, 0x29, 0xbf, 0x48, 0x6a},
       {0x13, 0xf0, 0x41, 0x54, 0xd6, 0x9d, 0x1a, 0xe5}
    },
    {
       {0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16},
       {0x3b, 0xdd, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
       {0x2e, 0xed, 0xda, 0x93, 0xff, 0xd3, 0x9c, 0x79}
    },
    {
       {0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f},
       {0x26, 0x95, 0x5f, 0x68, 0x35, 0xaf, 0x60, 0x9a},
       {0xd8, 0x87, 0xe0, 0x39, 0x3c, 0x2d, 0xa6, 0xe3}
    },
    {
       {0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46},
       {0x16, 0x4d, 0x5e, 0x40, 0x4f, 0x27, 0x52, 0x32},
       {0x5f, 0x99, 0xd0, 0x4f, 0x5b, 0x16, 0x39, 0x69}
    },
    {
       {0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e},
       {0x6b, 0x05, 0x6e, 0x18, 0x75, 0x9f, 0x5c, 0xca},
       {0x4a, 0x05, 0x7a, 0x3b, 0x24, 0xd3, 0x97, 0x7b}
    },
    {
       {0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76},
       {0x00, 0x4b, 0xd6, 0xef, 0x09, 0x17, 0x60, 0x62},
       {0x45, 0x20, 0x31, 0xc1, 0xe4, 0xfa, 0xda, 0x8e}
    },
    {
       {0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07},
       {0x48, 0x0d, 0x39, 0x00, 0x6e, 0xe7, 0x62, 0xf2},
       {0x75, 0x55, 0xae, 0x39, 0xf5, 0x9b, 0x87, 0xbd}
    },
    {
       {0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f},
       {0x43, 0x75, 0x40, 0xc8, 0x69, 0x8f, 0x3c, 0xfa},
       {0x53, 0xc5, 0x5f, 0x9c, 0xb4, 0x9f, 0xc0, 0x19}
    },
    {
       {0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7},
       {0x07, 0x2d, 0x43, 0xa0, 0x77, 0x07, 0x52, 0x92},
       {0x7a, 0x8e, 0x7b, 0xfa, 0x93, 0x7e, 0x89, 0xa3}
    },
    {
       {0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf},
       {0x02, 0xfe, 0x55, 0x77, 0x81, 0x17, 0xf1, 0x2a},
       {0xcf, 0x9c, 0x5d, 0x7a, 0x49, 0x86, 0xad, 0xb5}
    },
    {
       {0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6},
       {0x1d, 0x9d, 0x5c, 0x50, 0x18, 0xf7, 0x28, 0xc2},
       {0xd1, 0xab, 0xb2, 0x90, 0x65, 0x8b, 0xc7, 0x78}
    },
    {
       {0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef},
       {0x30, 0x55, 0x32, 0x28, 0x6d, 0x6f, 0x29, 0x5a},
       {0x55, 0xcb, 0x37, 0x74, 0xd1, 0x3e, 0xf2, 0x01}
    },
    {
       {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0xfa, 0x34, 0xec, 0x48, 0x47, 0xb2, 0x68, 0xb2}
    },
    {
       {0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e},
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0xa7, 0x90, 0x79, 0x51, 0x08, 0xea, 0x3c, 0xae}
    },
    {
       {0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1, 0xfe},
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0xc3, 0x9e, 0x07, 0x2d, 0x9f, 0xac, 0x63, 0x1d}
    },
    {
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
       {0x01, 0x49, 0x33, 0xe0, 0xcd, 0xaf, 0xf6, 0xe4}
    },
    {
       {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0xf2, 0x1e, 0x9a, 0x77, 0xb7, 0x1c, 0x49, 0xbc}
    },
    {
       {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
       {0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9a}
    },
    {
       {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
       {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
       {0x6b, 0x5c, 0x5a, 0x9c, 0x5d, 0x9e, 0x0a, 0x5a}
    },
  };

  for (int i = 0; i < 34; i++)
    {
      mobius::crypt::cipher_blowfish cipher (values1[i][0]);
      test.ASSERT_EQUAL (cipher.encrypt (values1[i][1]), values1[i][2]);
      test.ASSERT_EQUAL (cipher.decrypt (values1[i][2]), values1[i][1]);
    }
  
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // mode ECB - variable key length
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray plaintext = { 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };

  const mobius::bytearray values2[24][2] =    // key, ciphertext
  {
    {
       {0xf0},
       {0xf9, 0xad, 0x59, 0x7c, 0x49, 0xdb, 0x00, 0x5e}
    },
    {
       {0xf0, 0xe1},
       {0xe9, 0x1d, 0x21, 0xc1, 0xd9, 0x61, 0xa6, 0xd6}
    },
    {
       {0xf0, 0xe1, 0xd2},
       {0xe9, 0xc2, 0xb7, 0x0a, 0x1b, 0xc6, 0x5c, 0xf3}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3},
       {0xbe, 0x1e, 0x63, 0x94, 0x08, 0x64, 0x0f, 0x05}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4},
       {0xb3, 0x9e, 0x44, 0x48, 0x1b, 0xdb, 0x1e, 0x6e}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5},
       {0x94, 0x57, 0xaa, 0x83, 0xb1, 0x92, 0x8c, 0x0d}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96},
       {0x8b, 0xb7, 0x70, 0x32, 0xf9, 0x60, 0x62, 0x9d}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87},
       {0xe8, 0x7a, 0x24, 0x4e, 0x2c, 0xc8, 0x5e, 0x82}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78},
       {0x15, 0x75, 0x0e, 0x7a, 0x4f, 0x4e, 0xc5, 0x77}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69},
       {0x12, 0x2b, 0xa7, 0x0b, 0x3a, 0xb6, 0x4a, 0xe0}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a},
       {0x3a, 0x83, 0x3c, 0x9a, 0xff, 0xc5, 0x37, 0xf6}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b},
       {0x94, 0x09, 0xda, 0x87, 0xa9, 0x0f, 0x6b, 0xf2}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c},
       {0x88, 0x4f, 0x80, 0x62, 0x50, 0x60, 0xb8, 0xb4}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d},
       {0x1f, 0x85, 0x03, 0x1c, 0x19, 0xe1, 0x19, 0x68}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e},
       {0x79, 0xd9, 0x37, 0x3a, 0x71, 0x4c, 0xa3, 0x4f}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f},
       {0x93, 0x14, 0x28, 0x87, 0xee, 0x3b, 0xe1, 0x5c}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00},
       {0x03, 0x42, 0x9e, 0x83, 0x8c, 0xe2, 0xd1, 0x4b}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11},
       {0xa4, 0x29, 0x9e, 0x27, 0x46, 0x9f, 0xf6, 0x7b}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22},
       {0xaf, 0xd5, 0xae, 0xd1, 0xc1, 0xbc, 0x96, 0xa8}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33},
       {0x10, 0x85, 0x1c, 0x0e, 0x38, 0x58, 0xda, 0x9f}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33, 0x44},
       {0xe6, 0xf5, 0x1e, 0xd7, 0x9b, 0x9d, 0xb2, 0x1f}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
       {0x64, 0xa6, 0xe1, 0x4a, 0xfd, 0x36, 0xb4, 0x6f}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
       {0x80, 0xc7, 0xd7, 0xd4, 0x5a, 0x54, 0x79, 0xad}
    },
    {
       {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77},
       {0x05, 0x04, 0x4b, 0x62, 0xfa, 0x52, 0xd0, 0x80}
    },
  };

  for (int i = 0; i < 24; i++)
    {
      mobius::crypt::cipher_blowfish cipher (values2[i][0]);
      test.ASSERT_EQUAL (cipher.encrypt (plaintext), values2[i][1]);
      test.ASSERT_EQUAL (cipher.decrypt (values2[i][1]), plaintext);
    }
  
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // mode CBC
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const mobius::bytearray key2 = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
  const mobius::bytearray iv = {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10};
  const mobius::bytearray plaintext2 = {0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x20, 0x4e, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x00, 0x00, 0x00, 0x00};
  const mobius::bytearray ciphertext2 = {0x6b, 0x77, 0xb4, 0xd6, 0x30, 0x06, 0xde, 0xe6, 0x05, 0xb1, 0x56, 0xe2, 0x74, 0x03, 0x97, 0x93, 0x58, 0xde, 0xb9, 0xe7, 0x15, 0x46, 0x16, 0xd9, 0x59, 0xf1, 0x65, 0x2b, 0xd5, 0xff, 0x92, 0xcc};

  mobius::crypt::cipher_blowfish cipher2 (key2, "cbc", iv);
  test.ASSERT_EQUAL (cipher2.encrypt (plaintext2), ciphertext2);

  mobius::crypt::cipher_blowfish cipher3 (key2, "cbc", iv);
  test.ASSERT_EQUAL (cipher3.decrypt (ciphertext2), plaintext2);

  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", "cbc");
  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}, "cbc");
  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_generate_hash_nt ()
{
  mobius::unittest test ("mobius::crypt::generate_hash_nt");

  auto hash_nt = [] (const std::string& pwd) -> std::string { return mobius::crypt::generate_hash_nt (pwd).to_hexstring (); };

  test.ASSERT_EQUAL (hash_nt (""), "31d6cfe0d16ae931b73c59d7e0c089c0");
  test.ASSERT_EQUAL (hash_nt ("123456"), "32ed87bdb5fdc5e9cba88547376818d4");
  test.ASSERT_EQUAL (hash_nt ("user"), "57d583aa46d571502aad4bb7aea09c70");

  test.end ();
}

static void
testcase_generate_hash_lm ()
{
  mobius::unittest test ("mobius::crypt::generate_hash_lm");

  auto hash_lm = [] (const std::string& pwd) -> std::string { return mobius::crypt::generate_hash_lm (pwd).to_hexstring (); };

  test.ASSERT_EQUAL (hash_lm (""), "aad3b435b51404eeaad3b435b51404ee");
  test.ASSERT_EQUAL (hash_lm ("123456"), "44efce164ab921caaad3b435b51404ee");
  test.ASSERT_EQUAL (hash_lm ("user"), "22124ea690b83bfbaad3b435b51404ee");
  test.ASSERT_EQUAL (hash_lm ("USER"), "22124ea690b83bfbaad3b435b51404ee");

  test.end ();
}

static void
testcase_generate_hash_msdcc1 ()
{
  mobius::unittest test ("mobius::crypt::generate_hash_msdcc1");

  auto hash_msdcc1 = [] (const std::string& pwd, const std::string& username) -> std::string { return mobius::crypt::generate_hash_msdcc1 (pwd, username).to_hexstring (); };

  // test cases from JTR (http://www.openwall.com/john)
  test.ASSERT_EQUAL (hash_msdcc1 ("", "root"), "176a4c2bd45ac73687676c2f09045353");
  test.ASSERT_EQUAL (hash_msdcc1 ("test2", "test2"), "ab60bdb4493822b175486810ac2abe63");
  test.ASSERT_EQUAL (hash_msdcc1 ("test2", "TEST2"), "ab60bdb4493822b175486810ac2abe63");

  test.end ();
}

static void
testcase_generate_hash_msdcc2 ()
{
  mobius::unittest test ("mobius::crypt::generate_hash_msdcc2");

  auto hash_msdcc2 = [] (const std::string& pwd, const std::string& username, int i) -> std::string { return mobius::crypt::generate_hash_msdcc2 (pwd, username, i).to_hexstring (); };

  // test cases from JTR (http://www.openwall.com/john)
  test.ASSERT_EQUAL (hash_msdcc2 ("", "bin", 10240), "c0cbe0313a861062e29f92ede58f9b36");
  test.ASSERT_EQUAL (hash_msdcc2 ("test1", "test1", 10240), "607bbe89611e37446e736f7856515bf8");
  test.ASSERT_EQUAL (hash_msdcc2 ("qerwt", "Joe", 10240), "e09b38f84ab0be586b730baf61781e30");
  test.ASSERT_EQUAL (hash_msdcc2 ("12345", "Joe", 10240), "6432f517a900b3fc34ffe57f0f346e16");

  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_md4 ()
{
  mobius::unittest test ("mobius::crypt::hash_md4");

  // RFC 1320 - section A.5
  mobius::crypt::hash_md4 h1;
  test.ASSERT_EQUAL (get_hex_digest (h1), "31d6cfe0d16ae931b73c59d7e0c089c0");

  mobius::crypt::hash_md4 h2;
  h2.update ("a");
  test.ASSERT_EQUAL (get_hex_digest (h2), "bde52cb31de33e46245e05fbdbd6fb24");

  mobius::crypt::hash_md4 h3;
  h3.update ("abc");
  test.ASSERT_EQUAL (get_hex_digest (h3), "a448017aaf21d8525fc10ae87aa6729d");

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

  mobius::crypt::hash_md4 h5;
  h5.update ("abcdefghijklmnopqrstuvwxyz");
  test.ASSERT_EQUAL (get_hex_digest (h5), "d79e1c308aa5bbcdeea8ed63df412da9");

  mobius::crypt::hash_md4 h6;
  h6.update ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
  test.ASSERT_EQUAL (get_hex_digest (h6), "043f8582f241db351ce627e153e7f0e4");

  mobius::crypt::hash_md4 h7;
  h7.update ("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
  test.ASSERT_EQUAL (get_hex_digest (h7), "e33b4ddc9c38f2199c3e7b164fcc0536");

  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 ();
}

static void
testcase_hmac ()
{
  mobius::unittest test ("mobius::crypt::hmac");
  mobius::bytearray data (50);

  // RFC 2202 - section 2
  mobius::crypt::hmac h1 ("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b");
  h1.update ("Hi There");
  test.ASSERT_EQUAL (get_hex_digest (h1), "9294727a3638bb1c13f48ef8158bfc9d");

  mobius::crypt::hmac h2 ("Jefe");
  h2.update ("what do ya want for nothing?");
  test.ASSERT_EQUAL (get_hex_digest (h2), "750c783e6ab0b503eaa86e310a5db738");

  mobius::crypt::hmac h3 ("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa");
  data.fill (0xdd);
  h3.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h3), "56be34521d144c88dbb8c733f0e8b3f6");

  mobius::crypt::hmac h4 ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19");
  data.fill (0xcd);
  h4.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h4), "697eaf0aca3a3aea3a75164746ffaa79");

  mobius::crypt::hmac h5 ("\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c");
  h5.update ("Test With Truncation");
  test.ASSERT_EQUAL (get_hex_digest (h5), "56461ef2342edc00f9bab995690efd4c");

  mobius::bytearray k6 (80);
  k6.fill (0xaa);
  mobius::crypt::hmac h6 (k6);
  h6.update ("Test Using Larger Than Block-Size Key - Hash Key First");
  test.ASSERT_EQUAL (get_hex_digest (h6), "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd");

  mobius::crypt::hmac h7 (k6);
  h7.update ("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
  test.ASSERT_EQUAL (get_hex_digest (h7), "6f630fad67cda0ee1fb1f562db3aa53e");

  // RFC 2202 - section 3
  mobius::crypt::hmac h8 ("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", "sha1");
  h8.update ("Hi There");
  test.ASSERT_EQUAL (get_hex_digest (h8), "b617318655057264e28bc0b6fb378c8ef146be00");

  mobius::crypt::hmac h9 ("Jefe", "sha1");
  h9.update ("what do ya want for nothing?");
  test.ASSERT_EQUAL (get_hex_digest (h9), "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");

  mobius::crypt::hmac h10 ("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", "sha1");
  data.fill (0xdd);
  h10.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h10), "125d7342b9ac11cd91a39af48aa17b4f63f175d3");

  mobius::crypt::hmac h11 ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", "sha1");
  data.fill (0xcd);
  h11.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h11), "4c9007f4026250c6bc8414f9bf50c86c2d7235da");

  mobius::crypt::hmac h12 ("\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", "sha1");
  h12.update ("Test With Truncation");
  test.ASSERT_EQUAL (get_hex_digest (h12), "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04");

  mobius::crypt::hmac h13 (k6, "sha1");
  h13.update ("Test Using Larger Than Block-Size Key - Hash Key First");
  test.ASSERT_EQUAL (get_hex_digest (h13), "aa4ae5e15272d00e95705637ce8a3b55ed402112");

  mobius::crypt::hmac h14 (k6, "sha1");
  h14.update ("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data");
  test.ASSERT_EQUAL (get_hex_digest (h14), "e8e99d0f45237d786d6bbaa7965c7808bbff1a91");

  // RFC 4231 - section 4.2
  mobius::bytearray k15 ("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b");

  mobius::crypt::hmac h15a (k15, "sha2_224");
  h15a.update ("Hi There");
  test.ASSERT_EQUAL (get_hex_digest (h15a), "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22");

  mobius::crypt::hmac h15b (k15, "sha2_256");
  h15b.update ("Hi There");
  test.ASSERT_EQUAL (get_hex_digest (h15b), "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");

  mobius::crypt::hmac h15c (k15, "sha2_384");
  h15c.update ("Hi There");
  test.ASSERT_EQUAL (get_hex_digest (h15c), "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6");

  mobius::crypt::hmac h15d (k15, "sha2_512");
  h15d.update ("Hi There");
  test.ASSERT_EQUAL (get_hex_digest (h15d), "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854");

  // RFC 4231 - section 4.3
  mobius::crypt::hmac h16a ("Jefe", "sha2_224");
  h16a.update ("what do ya want for nothing?");
  test.ASSERT_EQUAL (get_hex_digest (h16a), "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44");

  mobius::crypt::hmac h16b ("Jefe", "sha2_256");
  h16b.update ("what do ya want for nothing?");
  test.ASSERT_EQUAL (get_hex_digest (h16b), "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843");

  mobius::crypt::hmac h16c ("Jefe", "sha2_384");
  h16c.update ("what do ya want for nothing?");
  test.ASSERT_EQUAL (get_hex_digest (h16c), "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649");

  mobius::crypt::hmac h16d ("Jefe", "sha2_512");
  h16d.update ("what do ya want for nothing?");
  test.ASSERT_EQUAL (get_hex_digest (h16d), "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737");

  // RFC 4231 - section 4.4
  mobius::bytearray k17 ("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa");
  data.fill (0xdd);

  mobius::crypt::hmac h17a (k17, "sha2_224");
  h17a.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h17a), "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea");

  mobius::crypt::hmac h17b (k17, "sha2_256");
  h17b.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h17b), "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe");

  mobius::crypt::hmac h17c (k17, "sha2_384");
  h17c.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h17c), "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27");

  mobius::crypt::hmac h17d (k17, "sha2_512");
  h17d.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h17d), "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb");

  // RFC 4231 - section 4.5
  mobius::bytearray k18 ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19");
  data.fill (0xcd);

  mobius::crypt::hmac h18a (k18, "sha2_224");
  h18a.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h18a), "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a");

  mobius::crypt::hmac h18b (k18, "sha2_256");
  h18b.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h18b), "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b");

  mobius::crypt::hmac h18c (k18, "sha2_384");
  h18c.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h18c), "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb");

  mobius::crypt::hmac h18d (k18, "sha2_512");
  h18d.update (data);
  test.ASSERT_EQUAL (get_hex_digest (h18d), "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd");

  // RFC 4231 - section 4.7
  mobius::bytearray k19 (131);
  mobius::bytearray data19 ("Test Using Larger Than Block-Size Key - Hash Key First");
  k19.fill (0xaa);

  mobius::crypt::hmac h19a (k19, "sha2_224");
  h19a.update (data19);
  test.ASSERT_EQUAL (get_hex_digest (h19a), "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e");

  mobius::crypt::hmac h19b (k19, "sha2_256");
  h19b.update (data19);
  test.ASSERT_EQUAL (get_hex_digest (h19b), "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54");

  mobius::crypt::hmac h19c (k19, "sha2_384");
  h19c.update (data19);
  test.ASSERT_EQUAL (get_hex_digest (h19c), "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952");

  mobius::crypt::hmac h19d (k19, "sha2_512");
  h19d.update (data19);
  test.ASSERT_EQUAL (get_hex_digest (h19d), "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598");

  // RFC 4231 - section 4.8
  mobius::bytearray k20 = k19;
  mobius::bytearray data20 ("This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.");

  mobius::crypt::hmac h20a (k20, "sha2_224");
  h20a.update (data20);
  test.ASSERT_EQUAL (get_hex_digest (h20a), "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1");

  mobius::crypt::hmac h20b (k20, "sha2_256");
  h20b.update (data20);
  test.ASSERT_EQUAL (get_hex_digest (h20b), "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2");

  mobius::crypt::hmac h20c (k20, "sha2_384");
  h20c.update (data20);
  test.ASSERT_EQUAL (get_hex_digest (h20c), "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e");

  mobius::crypt::hmac h20d (k20, "sha2_512");
  h20d.update (data20);
  test.ASSERT_EQUAL (get_hex_digest (h20d), "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58");

  test.end ();
}

void
unittest_crypt ()
{
  testcase_cipher_3des ();
  testcase_cipher_aes ();
  testcase_cipher_blowfish ();
  testcase_cipher_des ();
  testcase_cipher_rc4 ();
  testcase_cipher_rot13 ();
  testcase_cipher_zip ();
  testcase_generate_hash_nt ();
  testcase_generate_hash_lm ();
  testcase_generate_hash_msdcc1 ();
  testcase_generate_hash_msdcc2 ();
  testcase_hash_adler32 ();
  testcase_hash_crc32 ();
  testcase_hash_md4 ();
  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 ();
  testcase_hmac ();
}
