#include "des.h"
#include <stdio.h>

typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
typedef unsigned long uint64_t;

#define NUM_TESTS 34

static uint64_t key_data[NUM_TESTS]={
  0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0x3000000000000000ULL,
  0x1111111111111111ULL, 0x0123456789ABCDEFULL, 0x1111111111111111ULL,
  0x0000000000000000ULL, 0xFEDCBA9876543210ULL, 0x7CA110454A1A6E57ULL,
  0x0131D9619DC1376EULL, 0x07A1133E4A0B2686ULL, 0x3849674C2602319EULL,
  0x04B915BA43FEB5B6ULL, 0x0113B970FD34F2CEULL, 0x0170F175468FB5E6ULL,
  0x43297FAD38E373FEULL, 0x07A7137045DA2A16ULL, 0x04689104C2FD3B2FULL,
  0x37D06BB516CB7546ULL, 0x1F08260D1AC2465EULL, 0x584023641ABA6176ULL,
  0x025816164629B007ULL, 0x49793EBC79B3258FULL, 0x4FB05E1515AB73A7ULL,
  0x49E95D6D4CA229BFULL, 0x018310DC409B26D6ULL, 0x1C587F1C13924FEFULL,
  0x0101010101010101ULL, 0x1F1F1F1F0E0E0E0EULL, 0xE0FEE0FEF1FEF1FEULL,
  0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0x0123456789ABCDEFULL,
  0xFEDCBA9876543210ULL};

static uint64_t plain_data[NUM_TESTS]={
  0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0x1000000000000001ULL,
  0x1111111111111111ULL, 0x1111111111111111ULL, 0x0123456789ABCDEFULL,
  0x0000000000000000ULL, 0x0123456789ABCDEFULL, 0x01A1D6D039776742ULL,
  0x5CD54CA83DEF57DAULL, 0x0248D43806F67172ULL, 0x51454B582DDF440AULL,
  0x42FD443059577FA2ULL, 0x059B5E0851CF143AULL, 0x0756D8E0774761D2ULL,
  0x762514B829BF486AULL, 0x3BDD119049372802ULL, 0x26955F6835AF609AULL,
  0x164D5E404F275232ULL, 0x6B056E18759F5CCAULL, 0x004BD6EF09176062ULL,
  0x480D39006EE762F2ULL, 0x437540C8698F3CFAULL, 0x072D43A077075292ULL,
  0x02FE55778117F12AULL, 0x1D9D5C5018F728C2ULL, 0x305532286D6F295AULL,
  0x0123456789ABCDEFULL, 0x0123456789ABCDEFULL, 0x0123456789ABCDEFULL,
  0xFFFFFFFFFFFFFFFFULL, 0x0000000000000000ULL, 0x0000000000000000ULL,
  0xFFFFFFFFFFFFFFFFULL};

static uint64_t cipher_data[NUM_TESTS]={
  0x8CA64DE9C1B123A7ULL, 0x7359B2163E4EDC58ULL, 0x958E6E627A05557BULL,
  0xF40379AB9E0EC533ULL, 0x17668DFC7292532DULL, 0x8A5AE1F81AB8F2DDULL,
  0x8CA64DE9C1B123A7ULL, 0xED39D950FA74BCC4ULL, 0x690F5B0D9A26939BULL,
  0x7A389D10354BD271ULL, 0x868EBB51CAB4599AULL, 0x7178876E01F19B2AULL,
  0xAF37FB421F8C4095ULL, 0x86A560F10EC6D85BULL, 0x0CD3DA020021DC09ULL,
  0xEA676B2CB7DB2B7AULL, 0xDFD64A815CAF1A0FULL, 0x5C513C9C4886C088ULL,
  0x0A2AEEAE3FF4AB77ULL, 0xEF1BF03E5DFA575AULL, 0x88BF0DB6D70DEE56ULL,
  0xA1F9915541020B56ULL, 0x6FBF1CAFCFFD0556ULL, 0x2F22E49BAB7CA1ACULL,
  0x5A6B612CC26CCE4AULL, 0x5F4C038ED12B2E41ULL, 0x63FAC0D034D9F793ULL,
  0x617B3A0CE8F07100ULL, 0xDB958605F8C8C606ULL, 0xEDBFD1C66C29CCC7ULL,
  0x355550B2150E2451ULL, 0xCAAAAF4DEAF1DBAEULL, 0xD5D44FF720683D0DULL,
  0x2A2BB008DF97C2F2ULL};


static inline uint32_t endian_swap32(uint32_t x)
{
  return (((x >> 24) & 0x000000ff) |
          ((x >> 8 ) & 0x0000ff00) |
          ((x << 8 ) & 0x00ff0000) |
          ((x << 24) & 0xff000000));
}

static inline uint64_t endian_swap64(uint64_t x)
{
  return (((uint64_t)endian_swap32(x      ) << 32) |
          ((uint64_t)endian_swap32(x >> 32)      ));
}

int main()
{
  uint32_t cipher = 1;
  uint64_t in64 = 0x5CD54CA83DEF57DAULL;
  uint64_t out64 = 0x0;
  uint64_t key64 = 0x0131D9619DC1376EULL;

  uint32_t i;

  for (i=0;i<NUM_TESTS;i++)
  {
    in64 = plain_data[i];
    key64 = key_data[i];

/*
  if (cipher)
  {
  in[0] = key[2]^in[0];
  in[1] = key[3]^in[1];
  }
  else
  {
  in[0] = key[4]^in[0];
  in[1] = key[5]^in[1];
  }
*/
    uint64_t kne[16][2];
    uint64_t knd[16][2];

    union convert_u
    {
      uint64_t dword;
      uint32_t word[2];
      uint8_t  byte[8];
    } convert_t;

    convert_t.dword = endian_swap64(key64);

    deskey(kne, convert_t.byte, !cipher);
    deskey(knd, convert_t.byte, cipher);

    convert_t.dword = endian_swap64(in64);
    des(kne, convert_t.byte);

    out64 = convert_t.dword;
/*
  if (!cipher)
  {
  out[0] = key[2]^out[0];
  out[1] = key[3]^out[1];
  }
  else
  {
  out[0] = key[4]^out[0];
  out[1] = key[5]^out[1];
  }
*/

    printf("[%u] key: %16llx\n", i, key_data[i]);

    printf("p:\t%16llx c:\t%16llx\n", in64, endian_swap64(out64));
    if (endian_swap64(out64) != cipher_data[i])
    {
      printf("Cipher failed, expected %16llx\n", cipher_data[i]);
      return 0;
    }

    convert_t.dword = out64;
    des(knd, convert_t.byte);

    printf("c:\t%16llx p:\t%16llx\n", out64, endian_swap64(convert_t.dword));

    if (endian_swap64(convert_t.dword) != plain_data[i])
    {
      printf("Decipher failed, expected %16llx\n", plain_data[i]);
      return 0;
    }

    printf("------------------------------------------------\n");
  }

  printf("TEST OK\n");

  return 0;
}
