#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include <openssl/md5.h>

#define CDKEY_LENGTH 23
#define CDKEY_SIGNED_PART 17

static unsigned char CDKEY_SECRET[] = {0x4d,0x67,0xde,0xb9,0x60,0xce,0x38,0x30,0xe1,0xb7,0x40,0xe5,0xeb,0x39,0xe0,0x15};

static bool is_hex_digit(int c) {
    return c >= '0' && c <= '9' ||
           c >= 'A' && c <= 'Z';
}

static char *hexlify(const unsigned char *data, size_t len) {
    char *res = malloc(len * 2 + 1);
    for (size_t i = 0; i < len; i++) {
        sprintf(&res[i*2], "%02X", (unsigned) data[i]);
    }
    return res;
}

__attribute__((noinline))
static bool validate_cdkey(const char *cdkey) {
    if (strlen(cdkey) != CDKEY_LENGTH) {
        return false;
    }

    for (int i = 0; i < strlen(cdkey); i++) {
        switch (i) {
        case 5:
        case 11:
        case 17:
            if (cdkey[i] != '-') return false;
            break;
        default:
            if (!is_hex_digit(cdkey[i])) return false;
            break;
        }
    }

    MD5_CTX ctx;
    MD5_Init(&ctx);

    MD5_Update(&ctx, cdkey, CDKEY_SIGNED_PART);
    MD5_Update(&ctx, CDKEY_SECRET, sizeof(CDKEY_SECRET));

    static unsigned char digest[MD5_DIGEST_LENGTH];
    MD5_Final(digest, &ctx);

    char *hex_digest = hexlify(digest, sizeof(digest));

    for (int i = CDKEY_SIGNED_PART+1, j = 0; i < CDKEY_LENGTH; i++, j++) {
        if (cdkey[i] != hex_digest[j]) {
            free(hex_digest);
            return false;
        }
    }
    free(hex_digest);
    return true;
}

int main(void) {
    FILE *f = fopen("./cd-key.txt", "r");
    if (!f) {
        fprintf(stderr, "failed to open CD key file\n");
        return 1;
    }

    char cdkey[CDKEY_LENGTH+2];
    if (!fgets(cdkey, sizeof(cdkey), f)) {
        fprintf(stderr, "failed to read CD key file\n");
        return 1;
    }

    if (strlen(cdkey) > 0 && cdkey[strlen(cdkey)-1] == '\n') {
        cdkey[strlen(cdkey)-1] = '\0';
    }

    if (!validate_cdkey(cdkey)) {
        fprintf(stderr, "invalid cdkey\n");
        return 1;
    }

    fprintf(stderr, "Good job!\n");

    return 0;
}
