/*
 * Common code for cracking DiskCryptor.
 *
 * This file takes replicated but common code, shared between the CPU
 * and the GPU formats, and places it into one common location.
 */

#include "arch.h"
#include "memory.h"
#include "jumbo.h"
#include "johnswap.h"
#include "xts.h"
#include "diskcryptor_common.h"

// #define DEBUG 1

struct fmt_tests diskcryptor_tests[] = {
	/* AES XTS test vectors */
	{"$diskcryptor$0*[encoded data, 4096 characters]", "openwall"},
#ifndef DEBUG
	{"$diskcryptor$0*[encoded data, 4096 characters]", "openwall"},
	{"$diskcryptor$0*[encoded data, 4096 characters]", "openwall123"},
	/* Twofish XTS test vectors */
	{"$diskcryptor$0*[encoded data, 4096 characters]", "password"},
	/* Serpent XTS test vectors */
	{"$diskcryptor$0*[encoded data, 4096 characters]", "serpent"},
#endif
	{NULL}
};

int diskcryptor_valid(char *ciphertext, struct fmt_main *self)
{
	char *ctcopy, *keeptr, *p;
	int extra;

	if (strncmp(ciphertext, FORMAT_TAG, TAG_LENGTH) != 0)
		return 0;

	ctcopy = xstrdup(ciphertext);
	keeptr = ctcopy;

	ctcopy += TAG_LENGTH;
	if ((p = strtokm(ctcopy, "*")) == NULL) // type
		goto err;
	if (strcmp(p, "0"))
		goto err;
	if ((p = strtokm(NULL, "*")) == NULL) // header
		goto err;
	if (hexlenl(p, &extra) != 2048 * 2 || extra)
		goto err;

	MEM_FREE(keeptr);
	return 1;

err:
	MEM_FREE(keeptr);
	return 0;
}

void *diskcryptor_get_salt(char *ciphertext)
{
	static struct custom_salt cs;
	char *ctcopy = xstrdup(ciphertext);
	char *keeptr = ctcopy;
	char *p;
	int i;

	memset(&cs, 0, sizeof(struct custom_salt));
	ctcopy += TAG_LENGTH;
	p = strtokm(ctcopy, "*");
	cs.type = atoi(p);
	p = strtokm(NULL, "*");
	for (i = 0; i < 2048; i++)
		cs.header[i] = (atoi16[ARCH_INDEX(p[2 * i])] << 4) | atoi16[ARCH_INDEX(p[2 * i + 1])];

	memcpy(cs.salt, cs.header, 64);
	cs.iterations = 1000; // fixed as of version 1.1.846.118 (09.07.2014)
	cs.saltlen = 64;

	MEM_FREE(keeptr);
	return &cs;
}

unsigned int diskcryptor_iteration_count(void *salt)
{
	struct custom_salt *cs = (struct custom_salt*)salt;

	return cs->iterations;
}

int diskcryptor_decrypt_data(unsigned char *key, struct custom_salt *cur_salt)
{
	const int len = 96; /* up to 2048 */
	union {
		unsigned char uc[len];
		struct dc_header header;
	} out;
	int algorithm;

	for (algorithm = 0; algorithm < 3; algorithm++) {
		unsigned char tweak[16] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

		// Note: header.reserved is actually all zeroes but isn't for us because our XTS code is wonky?
		XTS_decrypt_custom_tweak(key, tweak, out.uc, cur_salt->header, len, 256, algorithm);
#if ARCH_LITTLE_ENDIAN==0
		out.header.alg_1 = JOHNSWAP(out.header.alg_1);
		out.header.version = (out.header.version >> 8) | (out.header.version << 8);
#endif
		if (!memcmp(out.uc + 64, "DCRP", 4) &&
		    (out.header.version == 2 || out.header.version == 1) &&
		    (out.header.alg_1 >= 0 && out.header.alg_1 <= 7))
			return 1;
	}

	return 0;
}
