/*
 *  Copyright 2004 Hans Leidekker
 *  Copyright 2006 Mike McCormack
 *
 *  Based on DES.c from libcifs
 *
 *  Copyright (C) 2003, 2004 by Christopher R. Hertel
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "windef.h"
#include "crypt.h"

static const unsigned char InitialPermuteMap[64] =
{
    57, 49, 41, 33, 25, 17,  9, 1,
    59, 51, 43, 35, 27, 19, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7,
    56, 48, 40, 32, 24, 16,  8, 0,
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6
 };

static const unsigned char KeyPermuteMap[56] =
{
    49, 42, 35, 28, 21, 14,  7,  0,
    50, 43, 36, 29, 22, 15,  8,  1,
    51, 44, 37, 30, 23, 16,  9,  2,
    52, 45, 38, 31, 55, 48, 41, 34,
    27, 20, 13,  6, 54, 47, 40, 33,
    26, 19, 12,  5, 53, 46, 39, 32,
    25, 18, 11,  4, 24, 17, 10,  3,
};

static const unsigned char KeyRotation[16] =
    { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };

static const unsigned char KeyCompression[48] =
{
    13, 16, 10, 23,  0,  4,  2, 27,
    14,  5, 20,  9, 22, 18, 11,  3,
    25,  7, 15,  6, 26, 19, 12,  1,
    40, 51, 30, 36, 46, 54, 29, 39,
    50, 44, 32, 47, 43, 48, 38, 55,
    33, 52, 45, 41, 49, 35, 28, 31
};

static const unsigned char DataExpansion[48] =
{
    31,  0,  1,  2,  3,  4,  3,  4,
     5,  6,  7,  8,  7,  8,  9, 10,
    11, 12, 11, 12, 13, 14, 15, 16,
    15, 16, 17, 18, 19, 20, 19, 20,
    21, 22, 23, 24, 23, 24, 25, 26,
    27, 28, 27, 28, 29, 30, 31,  0
};

static const unsigned char SBox[8][64] =
{
    {  /* S0 */
        14,  0,  4, 15, 13,  7,  1,  4,  2, 14, 15,  2, 11, 13,  8,  1,
         3, 10, 10,  6,  6, 12, 12, 11,  5,  9,  9,  5,  0,  3,  7,  8,
         4, 15,  1, 12, 14,  8,  8,  2, 13,  4,  6,  9,  2,  1, 11,  7,
        15,  5, 12, 11,  9,  3,  7, 14,  3, 10, 10,  0,  5,  6,  0, 13
    },
    {  /* S1 */
        15,  3,  1, 13,  8,  4, 14,  7,  6, 15, 11,  2,  3,  8,  4, 14,
         9, 12,  7,  0,  2,  1, 13, 10, 12,  6,  0,  9,  5, 11, 10,  5,
         0, 13, 14,  8,  7, 10, 11,  1, 10,  3,  4, 15, 13,  4,  1,  2,
         5, 11,  8,  6, 12,  7,  6, 12,  9,  0,  3,  5,  2, 14, 15,  9
    },
    {  /* S2 */
        10, 13,  0,  7,  9,  0, 14,  9,  6,  3,  3,  4, 15,  6,  5, 10,
         1,  2, 13,  8, 12,  5,  7, 14, 11, 12,  4, 11,  2, 15,  8,  1,
        13,  1,  6, 10,  4, 13,  9,  0,  8,  6, 15,  9,  3,  8,  0,  7,
        11,  4,  1, 15,  2, 14, 12,  3,  5, 11, 10,  5, 14,  2,  7, 12
    },
    {  /* S3 */
         7, 13, 13,  8, 14, 11,  3,  5,  0,  6,  6, 15,  9,  0, 10,  3,
         1,  4,  2,  7,  8,  2,  5, 12, 11,  1, 12, 10,  4, 14, 15,  9,
        10,  3,  6, 15,  9,  0,  0,  6, 12, 10, 11,  1,  7, 13, 13,  8,
        15,  9,  1,  4,  3,  5, 14, 11,  5, 12,  2,  7,  8,  2,  4, 14
    },
    {  /* S4 */
         2, 14, 12, 11,  4,  2,  1, 12,  7,  4, 10,  7, 11, 13,  6,  1,
         8,  5,  5,  0,  3, 15, 15, 10, 13,  3,  0,  9, 14,  8,  9,  6,
         4, 11,  2,  8,  1, 12, 11,  7, 10,  1, 13, 14,  7,  2,  8, 13,
        15,  6,  9, 15, 12,  0,  5,  9,  6, 10,  3,  4,  0,  5, 14,  3
    },
    {  /* S5 */
        12, 10,  1, 15, 10,  4, 15,  2,  9,  7,  2, 12,  6,  9,  8,  5,
         0,  6, 13,  1,  3, 13,  4, 14, 14,  0,  7, 11,  5,  3, 11,  8,
         9,  4, 14,  3, 15,  2,  5, 12,  2,  9,  8,  5, 12, 15,  3, 10,
         7, 11,  0, 14,  4,  1, 10,  7,  1,  6, 13,  0, 11,  8,  6, 13
    },
    {  /* S6 */
         4, 13, 11,  0,  2, 11, 14,  7, 15,  4,  0,  9,  8,  1, 13, 10,
         3, 14, 12,  3,  9,  5,  7, 12,  5,  2, 10, 15,  6,  8,  1,  6,
         1,  6,  4, 11, 11, 13, 13,  8, 12,  1,  3,  4,  7, 10, 14,  7,
        10,  9, 15,  5,  6,  0,  8, 15,  0, 14,  5,  2,  9,  3,  2, 12
    },
    {  /* S7 */
        13,  1,  2, 15,  8, 13,  4,  8,  6, 10, 15,  3, 11,  7,  1,  4,
        10, 12,  9,  5,  3,  6, 14, 11,  5,  0,  0, 14, 12,  9,  7,  2,
         7,  2, 11,  1,  4, 14,  1,  7,  9,  4, 12, 10, 14,  8,  2, 13,
         0, 15,  6, 12, 10,  9, 13,  0, 15,  3,  3,  5,  5,  6,  8, 11
    }
};

static const unsigned char PBox[32] =
{
    15,  6, 19, 20, 28, 11, 27, 16,
     0, 14, 22, 25,  4, 17, 30,  9,
     1,  7, 23, 13, 31, 26,  2,  8,
    18, 12, 29,  5, 21, 10,  3, 24
};

static const unsigned char FinalPermuteMap[64] =
{
     7, 39, 15, 47, 23, 55, 31, 63,
     6, 38, 14, 46, 22, 54, 30, 62,
     5, 37, 13, 45, 21, 53, 29, 61,
     4, 36, 12, 44, 20, 52, 28, 60,
     3, 35, 11, 43, 19, 51, 27, 59,
     2, 34, 10, 42, 18, 50, 26, 58,
     1, 33,  9, 41, 17, 49, 25, 57,
     0, 32,  8, 40, 16, 48, 24, 56
};

#define CLRBIT( STR, IDX ) ( (STR)[(IDX)/8] &= ~(0x01 << (7 - ((IDX)%8))) )
#define SETBIT( STR, IDX ) ( (STR)[(IDX)/8] |= (0x01 << (7 - ((IDX)%8))) )
#define GETBIT( STR, IDX ) (( ((STR)[(IDX)/8]) >> (7 - ((IDX)%8)) ) & 0x01)

static void Permute( unsigned char *dst, const unsigned char *src, const unsigned char *map, const int mapsize )
{
    int bitcount, i;

    for (i = 0; i < mapsize; i++)
        dst[i] = 0;

    bitcount = mapsize * 8;

    for (i = 0; i < bitcount; i++)
    {
        if (GETBIT( src, map[i] ))
            SETBIT( dst, i );
    }
}

static void KeyShiftLeft( unsigned char *key, const int numbits )
{
    int i;
    unsigned char keep = key[0];

    for (i = 0; i < numbits; i++)
    {
        int j;

        for (j = 0; j < 7; j++)
        {
            if (j && (key[j] & 0x80))
                key[j-1] |=  0x01;
            key[j] <<= 1;
        }

        if (GETBIT( key, 27 ))
        {
            CLRBIT( key, 27 );
            SETBIT( key, 55 );
        }

        if (keep & 0x80)
            SETBIT( key, 27 );

        keep <<= 1;
    }
}

static void KeyShiftRight( unsigned char *key, const int numbits )
{
    int i;
    unsigned char keep = key[6];

    for (i = 0; i < numbits; i++)
    {
        int j;

        for (j = 6; j >= 0; j--)
        {
            if (j!=6 && (key[j] & 0x01))
                key[j+1] |=  0x80;
            key[j] >>= 1;
        }

        if (GETBIT( key, 28 ))
        {
            CLRBIT( key, 28 );
            SETBIT( key, 0 );
        }

        if (keep & 0x01)
            SETBIT( key, 28 );

        keep >>= 1;
    }
}

static void sbox( unsigned char *dst, const unsigned char *src )
{
    int i;

    for (i = 0; i < 4; i++)
        dst[i] = 0;

    for (i = 0; i < 8; i++)
    {
        int j, Snum, bitnum;

        for (Snum = j = 0, bitnum = (i * 6); j < 6; j++, bitnum++)
        {
            Snum <<= 1;
            Snum |= GETBIT( src, bitnum );
        }

        if (0 == (i%2))
            dst[i/2] |= ((SBox[i][Snum]) << 4);
        else
            dst[i/2] |= SBox[i][Snum];
    }
}

static void xor( unsigned char *dst, const unsigned char *a, const unsigned char *b, const int count )
{
    int i;

    for (i = 0; i < count; i++)
        dst[i] = a[i] ^ b[i];
}

unsigned char *CRYPT_DEShash( unsigned char *dst, const unsigned char *key, const unsigned char *src )
{
    int i;
    unsigned char K[7];
    unsigned char D[8];

    Permute( K, key, KeyPermuteMap, 7 );
    Permute( D, src, InitialPermuteMap, 8 );

    for (i = 0; i < 16; i++)
    {
        int j;
        unsigned char *L = D;
        unsigned char *R = &(D[4]);
        unsigned char  Rexp[6];
        unsigned char  Rn[4];
        unsigned char  SubK[6];

        KeyShiftLeft( K, KeyRotation[i] );
        Permute( SubK, K, KeyCompression, 6 );

        Permute( Rexp, R, DataExpansion, 6 );
        xor( Rexp, Rexp, SubK, 6 );

        sbox( Rn, Rexp );
        Permute( Rexp, Rn, PBox, 4 );
        xor( Rn, L, Rexp, 4 );

        for (j = 0; j < 4; j++)
        {
            L[j] = R[j];
            R[j] = Rn[j];
        }
    }

    Permute( dst, D, FinalPermuteMap, 8 );

    return dst;
}

unsigned char *CRYPT_DESunhash( unsigned char *dst, const unsigned char *key, const unsigned char *src )
{
    int i;
    unsigned char K[7];
    unsigned char D[8];

    Permute( K, key, KeyPermuteMap, 7 );
    Permute( D, src, InitialPermuteMap, 8 );

    for (i = 0; i < 16; i++)
    {
        int j;
        unsigned char *L = D;
        unsigned char *R = &(D[4]);
        unsigned char  Rexp[6];
        unsigned char  Rn[4];
        unsigned char  SubK[6];

        Permute( SubK, K, KeyCompression, 6 );

        Permute( Rexp, R, DataExpansion, 6 );
        xor( Rexp, Rexp, SubK, 6 );

        sbox( Rn, Rexp );
        Permute( Rexp, Rn, PBox, 4 );
        xor( Rn, L, Rexp, 4 );

        for (j = 0; j < 4; j++)
        {
            L[j] = R[j];
            R[j] = Rn[j];
        }

        KeyShiftRight( K, KeyRotation[15 - i] );
    }

    Permute( dst, D, FinalPermuteMap, 8 );

    return dst;
}