/*
    NDS client for ncpfs
    Copyright (C) 1997  Arne de Bruijn

    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 of the License, 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, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Revision history:
    
	0.00  1997			Arne de Bruijn
						Initial release.
	0.01  1997			Petr Vandrovec <vandrove@vc.cvut.cz>
						Remove shuffle.

*/

#include <string.h>
#include "ndscrypt.h"
#include <ncp/ncplib.h>

static unsigned int rol16(unsigned int i, int c) {
    return ((i << c) & 65535) | ((unsigned int)(i & 65535) >> (16 - c));
}
static unsigned int ror16(unsigned int i, int c) {
    return ((unsigned int)(i & 65535) >> c) | ((i << (16 - c)) & 65535);
}

static unsigned char nwcryptdata[256]={
  0xD9,0x78,0xF9,0xC4,0x19,0xDD,0xB5,0xED,0x28,0xE9,0xFD,0x79,
  0x4A,0xA0,0xD8,0x9D,0xC6,0x7E,0x37,0x83,0x2B,0x76,0x53,0x8E,
  0x62,0x4C,0x64,0x88,0x44,0x8B,0xFB,0xA2,0x17,0x9A,0x59,0xF5,
  0x87,0xB3,0x4F,0x13,0x61,0x45,0x6D,0x8D,0x09,0x81,0x7D,0x32,
  0xBD,0x8F,0x40,0xEB,0x86,0xB7,0x7B,0x0B,0xF0,0x95,0x21,0x22,
  0x5C,0x6B,0x4E,0x82,0x54,0xD6,0x65,0x93,0xCE,0x60,0xB2,0x1C,
  0x73,0x56,0xC0,0x14,0xA7,0x8C,0xF1,0xDC,0x12,0x75,0xCA,0x1F,
  0x3B,0xBE,0xE4,0xD1,0x42,0x3D,0xD4,0x30,0xA3,0x3C,0xB6,0x26,
  0x6F,0xBF,0x0E,0xDA,0x46,0x69,0x07,0x57,0x27,0xF2,0x1D,0x9B,
  0xBC,0x94,0x43,0x03,0xF8,0x11,0xC7,0xF6,0x90,0xEF,0x3E,0xE7,
  0x06,0xC3,0xD5,0x2F,0xC8,0x66,0x1E,0xD7,0x08,0xE8,0xEA,0xDE,
  0x80,0x52,0xEE,0xF7,0x84,0xAA,0x72,0xAC,0x35,0x4D,0x6A,0x2A,
  0x96,0x1A,0xD2,0x71,0x5A,0x15,0x49,0x74,0x4B,0x9F,0xD0,0x5E,
  0x04,0x18,0xA4,0xEC,0xC2,0xE0,0x41,0x6E,0x0F,0x51,0xCB,0xCC,
  0x24,0x91,0xAF,0x50,0xA1,0xF4,0x70,0x39,0x99,0x7C,0x3A,0x85,
  0x23,0xB8,0xB4,0x7A,0xFC,0x02,0x36,0x5B,0x25,0x55,0x97,0x31,
  0x2D,0x5D,0xFA,0x98,0xE3,0x8A,0x92,0xAE,0x05,0xDF,0x29,0x10,
  0x67,0x6C,0xBA,0xC9,0xD3,0x00,0xE6,0xCF,0xE1,0x9E,0xA8,0x2C,
  0x63,0x16,0x01,0x3F,0x58,0xE2,0x89,0xA9,0x0D,0x38,0x34,0x1B,
  0xAB,0x33,0xFF,0xB0,0xBB,0x48,0x0C,0x5F,0xB9,0xB1,0xCD,0x2E,
  0xC5,0xF3,0xDB,0x47,0xE5,0xA5,0x9C,0x77,0x0A,0xA6,0x20,0x68,
  0xFE,0x7F,0xC1,0xAD};

#if 0
static unsigned char shuffle_table[32]=
 {0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
  0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
  0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
  0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};

static unsigned char shuffle_table2[256] =
 {0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
  0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
  0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
  0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
  0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
  0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
  0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
  0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
  0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
  0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
  0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
  0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
  0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
  0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
  0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
  0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
#endif

static unsigned char nwhashdata[256] =
 {0xBD,0x56,0xEA,0xF2,0xA2,0xF1,0xAC,0x2A,0xB0,0x93,0xD1,0x9C,
  0x1B,0x33,0xFD,0xD0,0x30,0x04,0xB6,0xDC,0x7D,0xDF,0x32,0x4B,
  0xF7,0xCB,0x45,0x9B,0x31,0xBB,0x21,0x5A,0x41,0x9F,0xE1,0xD9,
  0x4A,0x4D,0x9E,0xDA,0xA0,0x68,0x2C,0xC3,0x27,0x5F,0x80,0x36,
  0x3E,0xEE,0xFB,0x95,0x1A,0xFE,0xCE,0xA8,0x34,0xA9,0x13,0xF0,
  0xA6,0x3F,0xD8,0x0C,0x78,0x24,0xAF,0x23,0x52,0xC1,0x67,0x17,
  0xF5,0x66,0x90,0xE7,0xE8,0x07,0xB8,0x60,0x48,0xE6,0x1E,0x53,
  0xF3,0x92,0xA4,0x72,0x8C,0x08,0x15,0x6E,0x86,0x00,0x84,0xFA,
  0xF4,0x7F,0x8A,0x42,0x19,0xF6,0xDB,0xCD,0x14,0x8D,0x50,0x12,
  0xBA,0x3C,0x06,0x4E,0xEC,0xB3,0x35,0x11,0xA1,0x88,0x8E,0x2B,
  0x94,0x99,0xB7,0x71,0x74,0xD3,0xE4,0xBF,0x3A,0xDE,0x96,0x0E,
  0xBC,0x0A,0xED,0x77,0xFC,0x37,0x6B,0x03,0x79,0x89,0x62,0xC6,
  0xD7,0xC0,0xD2,0x7C,0x6A,0x8B,0x22,0xA3,0x5B,0x05,0x5D,0x02,
  0x75,0xD5,0x61,0xE3,0x18,0x8F,0x55,0x51,0xAD,0x1F,0x0B,0x5E,
  0x85,0xE5,0xC2,0x57,0x63,0xCA,0x3D,0x6C,0xB4,0xC5,0xCC,0x70,
  0xB2,0x91,0x59,0x0D,0x47,0x20,0xC8,0x4F,0x58,0xE0,0x01,0xE2,
  0x16,0x38,0xC4,0x6F,0x3B,0x0F,0x65,0x46,0xBE,0x7E,0x2D,0x7B,
  0x82,0xF9,0x40,0xB5,0x1D,0x73,0xF8,0xEB,0x26,0xC7,0x87,0x97,
  0x25,0x54,0xB1,0x28,0xAA,0x98,0x9D,0xA5,0x64,0x6D,0x7A,0xD4,
  0x10,0x81,0x44,0xEF,0x49,0xD6,0xAE,0x2E,0xDD,0x76,0x5C,0x2F,
  0xA7,0x1C,0xC9,0x09,0x69,0x9A,0x83,0xCF,0x29,0x39,0xB9,0xE9,
  0x4C,0xFF,0x43,0xAB};


void nwencrypt(const unsigned short *cryptbuf, const char *in, char *out) {
    int i, j;
    register unsigned int i1, i2, i3, i4;
    const unsigned short *p;

    i1 = WVAL_LH(in, 0);
    i2 = WVAL_LH(in, 2);
    i3 = WVAL_LH(in, 4);
    i4 = WVAL_LH(in, 6);
    p = cryptbuf;
    for (j = 3; j; j--) {
        for (i = (j == 2) ? 6 : 5; i; i--) {
            i1 = rol16(i1 + (*p++) + (i4 & i3) + (~i4 & i2), 1);
            i2 = rol16(i2 + (*p++) + (i1 & i4) + (~i1 & i3), 2);
            i3 = rol16(i3 + (*p++) + (i2 & i1) + (~i2 & i4), 3);
            i4 = rol16(i4 + (*p++) + (i3 & i2) + (~i3 & i1), 5);
        }
        if (j > 1) {
            i1 += cryptbuf[i4 & 63];
            i2 += cryptbuf[i1 & 63];
            i3 += cryptbuf[i2 & 63];
            i4 += cryptbuf[i3 & 63];
        }
    }
    WSET_LH(out, 0, i1);
    WSET_LH(out, 2, i2);
    WSET_LH(out, 4, i3);
    WSET_LH(out, 6, i4);
}

void nwdecrypt(const unsigned short *cryptbuf, const char *in, char *out) {
    int i, j;
    const unsigned short *p;
    register unsigned int i1, i2, i3, i4;

    i1 = WVAL_LH(in, 0);
    i2 = WVAL_LH(in, 2);
    i3 = WVAL_LH(in, 4);
    i4 = WVAL_LH(in, 6);
    p = cryptbuf + 64;
    for (j = 3; j; j--) {
        for (i = (j == 2) ? 6 : 5; i; i--) {
            i4 = ror16(i4, 5) - (~i3 & i1) - (i3 & i2) - (*--p);
            i3 = ror16(i3, 3) - (~i2 & i4) - (i2 & i1) - (*--p);
            i2 = ror16(i2, 2) - (~i1 & i3) - (i1 & i4) - (*--p);
            i1 = ror16(i1, 1) - (~i4 & i2) - (i4 & i3) - (*--p);
        }
        if (j > 1) {
            i4 -= cryptbuf[i3 & 63];
            i3 -= cryptbuf[i2 & 63];
            i2 -= cryptbuf[i1 & 63];
            i1 -= cryptbuf[i4 & 63];
        }
    }
    WSET_LH(out, 0, i1);
    WSET_LH(out, 2, i2);
    WSET_LH(out, 4, i3);
    WSET_LH(out, 6, i4);
}

void nwcryptinit(unsigned short *scryptbuf, const char *key) {
    int i;
    unsigned char cryptbuf[128], *p;

    memcpy(cryptbuf, key, 8);
    for (i = 0; i < 120; i++)
        cryptbuf[i + 8] =
         nwcryptdata[(unsigned char)(cryptbuf[i] + cryptbuf[i + 7]) & 255];
    cryptbuf[128 - 8] = nwcryptdata[(unsigned char)cryptbuf[128 - 8] & 255];
    for (i = 127 - 8; i >= 0; i--)
        cryptbuf[i] = nwcryptdata[(unsigned char)cryptbuf[i + 1] ^
         (unsigned char)cryptbuf[i + 8]];
    for (i = 0, p = cryptbuf; i < 64; i++, p += 2)
        scryptbuf[i] = (*p) | (*(p+1)) << 8;
}

void nwencryptblock(const char *cryptkey, const char *buf, int buflen, 
 char *outbuf) {
    int i;
    char nhash[8];
    unsigned short cryptbuf[64];

    nwcryptinit(cryptbuf, cryptkey);
    memset(nhash, 0, 8);
    while (buflen >= 8) {
        for (i = 0; i < 8; i++, buf++)
            nhash[i] ^= *buf;
        nwencrypt(cryptbuf, nhash, nhash);
        memcpy(outbuf, nhash, 8);
        outbuf += 8;
        buflen -= 8;
    }
    memset(cryptbuf, 0, sizeof(cryptbuf));
}

void nwdecryptblock(const char *cryptkey, const char *buf, int buflen, 
 char *outbuf) {
    int i;
    char nhash[16], *p;
    unsigned short cryptbuf[64];

    nwcryptinit(cryptbuf, cryptkey);
    memset(nhash, 0, 16);
    p = nhash;
    while (buflen >= 8) {
        memcpy(p, buf, 8);
        p = nhash + 8 - (p - nhash);
        nwdecrypt(cryptbuf, buf, outbuf);
        for (i = 0; i < 8; i++, outbuf++)
            *outbuf ^= p[i];
        buf += 8;
        buflen -= 8;
    }
    memset(cryptbuf, 0, sizeof(cryptbuf));
}

void nwhash1(char *hash, int hashlen, const char *data, int datalen) {
    unsigned char *hp, *hp1, *hend, c;
    const unsigned char *dp;

    hp1 = (hp = (unsigned char *)hash) + 1;
    hend = hp + hashlen;
    dp = (const unsigned char *)data;
    while (datalen--) {
        *hp = nwhashdata[*hp1 ^ *hp] ^ *dp++;
        hp = hp1++;
        if (hp1 == hend)
            hp1 = (unsigned char *)hash;
    }
    while (hp-- > (unsigned char *)hash) {
        hp1 = (unsigned char *)hash;
        c = *hp1++;
        while (*(hp1 - 1) = *hp1, ++hp1 < (unsigned char *)hash + hashlen);
        *(hp1 - 1) = c;
    }
}

void nwhash2(char *hashbuf, char c) {
    int i, j;
    char *p = hashbuf + hashbuf[0x40];

    p[0x20] = p[0x00] ^ (p[0x10] = c);
    hashbuf[0x41] = (p[0x30] ^= nwhashdata[(unsigned char)(c ^ hashbuf[0x41])]);
    if (!(hashbuf[0x40] = (hashbuf[0x40] + 1) & 15)) {
        c = 0;
        for (i = 18; i; i--)
            for (j = 48, p = hashbuf; j; j--)
                c = (*(p++) ^= nwhashdata[((unsigned char)c + j) & 255]);
    }
}

void nwhash2block(char *hashbuf, const char *data, int datalen) {
    while (datalen--)
        nwhash2(hashbuf, *data++);
}

void nwhash2end(char *hashbuf) {
    int i, j;

    for(j = i = 16 - hashbuf[0x40]; j; j--)
        nwhash2(hashbuf, i);
    for(i = 0x30; i < 0x40; i++)
        nwhash2(hashbuf, hashbuf[i]);
}

#if 0
void shuffle(const char *objid, const char *pwd, char *out) {
    unsigned char temp[32];
    int i, j, k;
    i = strlen(pwd);
    memset(temp, 0, 32);
    for (j = 0; j < i; j++)
        temp[j & 31] ^= pwd[j];
    if (i)
        for (j = i; j < 32; j += i) {
            temp[j++] = shuffle_table[j];
            k = 32 - j;
            memcpy(temp + j, pwd, (k > i) ? i : k);
        }
    for (i = 0; i < 32; i++)
        temp[i] ^= objid[i & 3];
    j = 0;
    for (k = 0; k < 2; k++)
        for (i = 0; i < 32; i++)
            (char)j += temp[i] = (temp[i] + j) ^
             (temp[(i + j) & 31] - shuffle_table[i]);
    for (i = 0; i < 16; i++)
        out[i] = shuffle_table2[temp[i * 2]] |
         (shuffle_table2[temp[i * 2 + 1]] << 4);
}
#endif

