/*
 * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
 *
 * Author:     Iceyer <me@iceyer.net>
 *
 * Maintainer: Iceyer <me@iceyer.net>
 *
 * 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 3 of the License, or
 * 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 <stdio.h>
#include <string.h>
#include <stdlib.h>

#include<openssl/md5.h>
#include <openssl/rand.h>
#include <linux/types.h>
#include <linux/uuid.h>

#include <stdbool.h>
#include <time.h>

typedef __u8 u8;
typedef __u16 u16;
typedef __u32 u32;
typedef __u64 u64;
typedef __time_t time_t;
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))

struct __attribute__((__packed__)) ActivationKey {
    unsigned char Salt[6];
    u16 Product;
    u32 Index;
    u8  Tail;
};

struct __attribute__((__packed__)) KeySignatureData {
    u32 Index;
    u8  Salt[6];
    u8  Tail;
    u8  Product[32];
};

void dump_bytes(const char *header, const unsigned char *bytes, int size)
{
    return;
    int i = 0;
    int line;
    unsigned char buffer[8] = {0};
    line = size / 8;
    printf("\n%s %d\n", header, size);
    for (i = 0; i < line; ++i) {
        printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
               bytes[i * 8 + 0], bytes[i * 8 + 1], bytes[i * 8 + 2], bytes[i * 8 + 3],
               bytes[i * 8 + 4], bytes[i * 8 + 5], bytes[i * 8 + 6], bytes[i * 8 + 7]);
    }

    if (size > line * 8) {
        memcpy(buffer, bytes + (line * 8), size - line * 8);
        printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
               buffer[0], buffer[1], buffer[2], buffer[3],
               buffer[4], buffer[5], buffer[6], buffer[7]);
    }
}

void rc4_init(unsigned char *s, unsigned char *key, unsigned long key_len)
{
    int i = 0, j = 0;
    char *k = (char *)malloc(key_len);
    memset(k, 0, key_len);
    unsigned char tmp = 0;
    for (i = 0; i < key_len; i++) {
        s[i] = i;
        k[i] = key[i % key_len];
    }
    for (i = 0; i < key_len; i++) {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
    free(k);
}

void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long len)
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < len; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];//交换s[x]和s[y]
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] ^= s[t];
    }
}

#define KEY_LEN  4096

const char *key = " ";

int activation_key_encrypt(unsigned char *pData, unsigned long len)
{
    unsigned char s[KEY_LEN] = { 0 }/*, s2[KEY_LEN] = { 0 }*/;//S-box
    rc4_init(s, (unsigned char *)key, strlen(key)); //已经完成了初始化
    rc4_crypt(s, (unsigned char *)pData, len); //加密
    return 0;
}

int activation_key_dencrypt(unsigned char *pData, unsigned long len)
{
    unsigned char s[KEY_LEN] = { 0 }/*, s2[KEY_LEN] = { 0 }*/;//S-box
    rc4_init(s, (unsigned char *)key, strlen(key)); //已经完成了初始化
    rc4_crypt(s, (unsigned char *)pData, len); //加密
    return 0;
}


int new_activation_key(const char *identification, unsigned char *bytes)
{
    //make md5 of product and edition
    int i = 0;
    int productID = 0x10000000;
    struct ActivationKey ak;
    unsigned char salt[7] = {0};
    unsigned char md[16];
    struct KeySignatureData ksd;
    u32 count = (1 & 0x00FFFFFF) | productID;
    unsigned char tailMask = 0xF0;
    RAND_bytes(salt, 7);
    salt[6] = salt[6] & tailMask;

    memset((unsigned char *)&ksd, 0, sizeof(ksd));
    memcpy(ksd.Product, identification, strlen(identification));
    memcpy(ksd.Salt, salt, ARRAY_SIZE(ksd.Salt));
    ksd.Index = count;
    memcpy((unsigned char *)&ksd.Tail, &salt[6], 1);

    dump_bytes(identification, (unsigned char *)md, 16);
    dump_bytes("KeySignatureData", (unsigned char *)&ksd, sizeof(ksd));

    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, (unsigned char *)&ksd, sizeof(ksd));
    MD5_Final(md, &ctx);

    memset((unsigned char *)&ak, 0, sizeof(ak));
    memcpy((unsigned char *)&ak.Product, md, 2);
    memcpy((unsigned char *)&ak.Index, (unsigned char *)&count, 4);
    memcpy((unsigned char *)&ak.Salt, salt, 6);
    memcpy((unsigned char *)&ak.Tail, &salt[6], 1);

    dump_bytes("Hash data:", (unsigned char *)md, 16);
    dump_bytes("Salt data:", (unsigned char *)&salt, 7);
    dump_bytes("ActivationKey data:", (unsigned char *)&ak, sizeof(ak));

    // Raw          0000 0000
    // Low Mask     0011 1001 = 0x39
    // High Mask    1100 0110 = 0xC6
    unsigned char lowMask = 0x39;
    unsigned char highMask = 0xC6;
    unsigned char *buf = (unsigned char *)&ak;
    for (i = 0; i < 6; ++i) {
        unsigned char headLow =  buf[i] & lowMask;
        unsigned char headHigh =  buf[i] & highMask;
        unsigned char tailLow =  buf[11 - i] & lowMask;
        unsigned char tailHigh =  buf[11 - i] & highMask;
        buf[i] = headHigh + tailLow;
        buf[11 - i] = tailHigh + headLow;
    }
    dump_bytes("Mix ActivationKey data:", (unsigned char *)&ak, sizeof(ak));

    activation_key_encrypt((unsigned char *)&ak, sizeof(ak));
    memcpy(bytes, (unsigned char *)&ak, sizeof(ak));
    return 0;
}

int getBin(const unsigned char *bytes, size_t index)
{
    size_t byteIndex = index % 8;
    size_t byteCount = index / 8;
    int ret = bytes[byteCount] >> (7 - byteIndex) & 0x01;
    return ret;
}

void setBin(unsigned char *bytes, const size_t index, bool value)
{
    size_t byteIndex = index % 8;
    size_t byteCount = index / 8;
    unsigned char cvalue = 0x01 << (7 - byteIndex);
    if (!value) {
        cvalue = 0x00;
    }
    bytes[byteCount] |= cvalue;
}

const char *byte_to_binary(int x)
{
    static char b[9];
    b[0] = '\0';

    int z;
    for (z = 128; z > 0; z >>= 1) {
        strcat(b, ((x & z) == z) ? "1" : "0");
    }

    return b;
}

char mapAN(int k)
{
    static char *maps = "2345678ABCDEFGHJKLMNOPQRSTUVWXYZ";
    return maps[k % 32];
}

char mapIndex(char c)
{
    switch (c) {
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
        return c - '2';
    case 'A':
    case 'B':
    case 'C':
    case 'D':
    case 'E':
    case 'F':
    case 'G':
    case 'H':
        return c - 'A' + 7;
    case 'J':
    case 'K':
    case 'L':
    case 'N':
    case 'M':
    case 'O':
    case 'P':
    case 'Q':
    case 'R':
    case 'S':
    case 'T':
    case 'U':
    case 'V':
    case 'W':
    case 'X':
    case 'Y':
    case 'Z':
        return c - 'J' + 15;
    }
    return 0;
}

void unpack(const char *key, struct ActivationKey *pak)
{
    //swap
    int i = 0;
    char rawkey[20];
    int raw[20];
    unsigned char lowMask = 0x39;
    unsigned char highMask = 0xC6;
    unsigned char *buf = (unsigned char *)pak;
    unsigned char tailMask = 0xF0;

    memset((unsigned char *)pak, 0, sizeof(*pak));
    for (i = 0; i < 20; ++i) {
        rawkey[i] = key [19 - i];
        raw[i]  = mapIndex(rawkey[i]);
    }

    for (i = 0; i < 20; ++i) {
        size_t base = (i) * 5;
        int b4 = (raw[i] >> 0) & 0x01;
        int b3 = (raw[i] >> 1) & 0x01;
        int b2 = (raw[i] >> 2) & 0x01;
        int b1 = (raw[i] >> 3) & 0x01;
        int b0 = (raw[i] >> 4) & 0x01;
        setBin((unsigned char *)pak, base + 0, b0);
        setBin((unsigned char *)pak, base + 1, b1);
        setBin((unsigned char *)pak, base + 2, b2);
        setBin((unsigned char *)pak, base + 3, b3);
        setBin((unsigned char *)pak, base + 4, b4);
    }

    activation_key_dencrypt((unsigned char *)pak, sizeof(*pak));

    dump_bytes("Unpack Mix ActivationKey data:", (unsigned char *)pak, sizeof(*pak));

    for (i = 0; i < 6; ++i) {
        unsigned char headLow =  buf[i] & lowMask;
        unsigned char headHigh =  buf[i] & highMask;
        unsigned char tailLow =  buf[11 - i] & lowMask;
        unsigned char tailHigh =  buf[11 - i] & highMask;
        buf[i] = headHigh + tailLow;
        buf[11 - i] = tailHigh + headLow;
    }

    pak->Tail = pak->Tail & tailMask;
    dump_bytes("Unpack ActivationKey data:", (unsigned char *)pak, sizeof(*pak));
}

void pack(const unsigned char *bytes, char *out)
{
    size_t i = 0;
    int key[32] = {0};
    for (i = 0; i < 20; ++i) {
        size_t base = (i) * 5;
        int b4 = getBin(bytes, base + 0) << 4;
        int b3 = getBin(bytes, base + 1) << 3;
        int b2 = getBin(bytes, base + 2) << 2;
        int b1 = getBin(bytes, base + 3) << 1;
        int b0 = getBin(bytes, base + 4);
        key[i] =  b4 + b3 + b2 + b1 + b0;
    }

    //swap
    for (i = 0; i < 10; ++i) {
        int tmp = key[i];
        key[i] = key [19 - i];
        key[19 - i] = tmp;
    }

    for (i = 0; i < 20; ++i) {
        out[i]  = mapAN(key[i]);
    }
}

int generate_activation_key(const char *copyright, const char *edition, const char *version,  char *key)
{
    unsigned char encrypt[32] = {0};
    unsigned char identification[2048] = {0};
    strcat(strcat(strcat(identification, copyright), edition), version);
    new_activation_key(identification, encrypt);
    pack(encrypt, key);
}

#define MD5_LENGTH     16

bool signature_check(const u8 *calc_sig, const u8 *cmp_sig, size_t max_size)
{
    size_t i  = 0;
    for (; i < max_size; ++i) {
        if (calc_sig[i] != cmp_sig[i]) {
            return false;
        }
    }
    return true;
}

bool verify_licese_serialkey(const char *key, const char *product)
{
    u8 buf[256];
    u8 output[MD5_LENGTH];
    // char *product;
    struct ActivationKey ak;
    struct KeySignatureData ksd;
    bool ret = false;

    unpack(key, &ak);

    dump_bytes("ActivationKey Salt", ak.Salt, ARRAY_SIZE(ak.Salt));
    memset((unsigned char *)&ksd, 0, sizeof(ksd));
    memcpy(ksd.Product, product, strlen(product));
    ksd.Index = ak.Index;
    memcpy(ksd.Salt, ak.Salt, ARRAY_SIZE(ksd.Salt));
    ksd.Tail = ak.Tail;

    dump_bytes("KeySignatureData", (unsigned char *)&ksd, sizeof(ksd));
    dump_bytes("KeySignatureData Salt", ksd.Salt, ARRAY_SIZE(ksd.Salt));

    memset(buf, 0x00, 256);
    memcpy(buf, (unsigned char *)&ksd, sizeof(ksd));
    memset(output, 0x00, MD5_LENGTH);

    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, (unsigned char *)&ksd, sizeof(ksd));
    MD5_Final(output, &ctx);

    dump_bytes("ActivationKey", (u8 *)&ak, sizeof(ak));
    dump_bytes("KeySignatureData Hash", output, MD5_LENGTH);

    ret = signature_check((u8 *)&ak.Product, output, 2);
    return ret;
}

int test_key()
{
    char pData[25] = "深度操作系统 V15";
    // activation_key_encrypt(pData);
    // dump_bytes("crypt data:", pData, 25);
    // activation_key_dencrypt(pData);
    // dump_bytes("dencrypt data:", pData, 25);
    char encrypt[32] = {0};
    char out[21] = {0};
    new_activation_key(pData, encrypt);
    unsigned char license_iv[16] = {
        0xFE, 0xA3, 0x66, 0x57, 0x99, 0x0C, 0xD0, 0xDC,
        0x72, 0xBC, 0x6B, 0x6C, 0x22, 0x6F, 0x67, 0x91,
    };
    pack(encrypt, out);

    unpack(out, NULL);

    printf("%s", out);
    return 0;
}
