/*
 * Common code for the Ethereum Wallet format.
 */

#include "arch.h"
#include "misc.h"
#include "common.h"
#include "ethereum_common.h"

struct fmt_tests ethereum_tests[] = {
	// https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition, v3 wallets
	{"$ethereum$p*262144*ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd*5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46*517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2", "testpassword"},
	// artificial hash for testing
	{"$ethereum$p*1024*30323330313838333730333538343831*6dcfca7cbd44eb3ca7f11162b996b98dd46fb68e1afb095686fe944fcbdb3b59*b3a766d8c1390462304af979c6f709ba54a23ff135d1ffcdef485dcc7f79b5f2", "6023"},
	// scrypt test vectors are disabled to ease CI testing
	// {"$ethereum$s*262144*1*8*ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19*d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c*2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097", "testpassword"},
	// {"$ethereum$s*262144*8*1*8de58242f1ab3111ed0ac43c35c8f0f26df783d3188cba906fd3cd33f7ded6b6*542e5f4563a5bb934c8c7caa39005e2cd8f1df731292392e4b006b1660e329df*2d05b6d31adb87243e6cdd9459912d6ddbf25e4bad7160def3e9dd6b3aa5989d", "openwall123"},
	// {"$ethereum$s*262144*8*1*089f8affe040ed0edaa7a238d505e235cf062262c1a4b8ce1b4fe1c8eb3f3eb7*2dcb06464c6fe2ccfd8eaef921fedceef68fa72e92d786e6272a8f66ecf9476c*9225bc94b0ff28e364a6f607d9213cd573eed178df19a9a08706603e61d93487", "password"},
	// {"$ethereum$s*262144*8*1*9daee6a42109e1facefe1f4c3db5452aba6d82f46d2fc15bde4923870e0f83e1*5be4a57d3629c36973186c78e2935e02cf38907c6531ae98400469f0291056be*ca58b99dfdf696af880c242410f22a55de2047b97e56562cd90ec1c38f1749c2", "openwall"},
	// MyEtherWallet (https://www.myetherwallet.com/)
	{"$ethereum$s*1024*8*1*8420bc934c1d6e3c016db11a22e31146b557ce254622135d128f4ed3fd8d86c8*3292ccbb6e3dfe1c1db71c941ec3f91b23726e726fc17708442d491bc824ae38*01af14f1ec3c7f457669c05ec4f56c914f2af39f23c6ff2a8c25d3e0d02500f8", "password123"},
	// MyEtherWallet sample created in December, 2017.
	{"$ethereum$s*1024*8*1*d9fab9e48297c413e10d2b991078d68466d748f04d538de54d92e99053b35a97*f265ba65dec4d32264b9b9a3a9aa44b792dbf1d4316dbee1c3b41bbc929b6949*ea8c06102ae63a6f214be9bdbe3855da2953263a280389a8fec31f1d75a5ebf4", "testing123"},
	// Mist 0.3.9
	// {"$ethereum$s*262144*8*1*cc48acb99a3c5e5494c46175dfdf13999a93b75285c04c289b1828e4a003c42c*193d41913eae708e545b2e9dbc40d345f3acf6e6ea8ffdfac63d606043c22267*da77eb1ebbb17e471cd397f5b6b31987800790b885d4c807336e6d8eff77b93b", "password123"},
	// presale wallets with 'bkp' generated by old https://github.com/ethereum/pyethsaletool
	{"$ethereum$w*9ace195a18b83b09da933f60f64fb4651e8413a5fdc12249724088d43dcd6d3c943f83d78d475e61177ca38965cb7794a2e1225250ae29134e60492871c79bf2*b9cfc3e87d22f37be858f97944844efbd32e5da5*ec97af5d7e240a559ee74f7a9e7312f2", "openwall"},
	{"$ethereum$w*4ab35fcb5c3101af70d5b3bf22829af3dbd48813273b17566ee364285c7bcfb2d52611a58e54d3e6be27e458073304e71e356afc4c97da0143910308f30563fe*c4dfdaa5288b477d0ff25a260ef32a24282cc4e2*97b3d2c2c8106507b87744f0b12f73c2", "password12345"},
	// "real-world" presale wallet, thanks to @FredericHeem, https://github.com/tagawa/website/blob/master/pyethtool/pyethtool.py#L61
	{"$ethereum$w*0b2fa2c63a8b5603e1104fbada15a609aa9736ed41db1678d91a9b1a1f7c1628e711dbc250c4c289b06b0faa56cd499dc4af9daf878202db22cc61df1a91c918314c77ce92e5c8b1265580edd79a852acf40fe2138653ac16524c08247381d9802cf5ef3f8c4baf69fb869f2b7fb796bae853cfbdc3c5b788a14e75f39e0cf7df2e90779181a5dd45cce8e8df938af3c6b6c8a92ce123338e6ed87eb16ff11a02cd4a2a07aff8a3a57097fcf137501e07a941a7ce9bc19498252d98769125fbd2c9a14f1c56212a6bf2a7e374474c60e7a3a1cf443ce8194c4c960474472d6ca761ada075169fa8c7017bf1274b99df898deb65f51ed8eb29fbc0997d69c9800ad9b8351155bec5d8e7f73e7e2882a6e1b62883d0158c44fed8e4412fb18e75757e1355aaadd8a2dab50ae40c800d032dc77d3e84904085d628b5a13b60317d6f12ede26b7b38e7c6805bea1d2e11e3a7d7153b76ebfd99ae2536dfdd071ff8111a86fbd63e7b17155162263ef45471ac5b4c517520572cd19410cc4cbde77914fad12326fe5a4cbd5fc4a297740d6b5e64001196b0531e2464b7e4cee77136a38844b94dc59a9a72eec3ff49bca3d5bf0c29652ee6ff028e22f8936aac58fa3cf05ce4c8de8883204e43b57e4ebed922ad7b3a8953042033d34d7e94bc8ff393d1df4c8b062f5228b4f9dbc5d157af96772af1ef2c84f6562049b1c44f0359c07f193623a8b0f1b7e34b31481ddf54a24128e5a21b929f57fd07f8911ad8eb8d8bfe928ae9dfa2d35663764161094552a43b43a0a43dca687d4d04b79c8dbb2d4f87b7d8e0805a77adddfd5149741faa594009639fb730dccfbee1f99286aaf94052c06a1c68efc29dcd57a8b1a421e*f9438b7121a15fd127ec0d8a72ee2b3e8da04a5a*74fdb879ece341dd590a769f2cd39d67", "password"},
	{NULL}
};

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

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

	ctcopy = strdup(ciphertext);
	keeptr = ctcopy;

	ctcopy += TAG_LENGTH;
	if ((p = strtokm(ctcopy, "*")) == NULL) // type
		goto err;
	if (*p != 'p' && *p != 's' && *p != 'w')
		goto err;
	if (*p == 'p') {
		if ((p = strtokm(NULL, "*")) == NULL)   // iterations
			goto err;
		if (!isdec(p))
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // salt
			goto err;
		if (hexlenl(p, &extra) > 64 * 2 || extra)
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // ciphertext
			goto err;
		if (hexlenl(p, &extra) != 64 || extra)
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // mac
			goto err;
		if (hexlenl(p, &extra) != 64 || extra)
			goto err;
	} else if (*p == 's') {
		if ((p = strtokm(NULL, "*")) == NULL)   // N
			goto err;
		if (!isdec(p))
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // r
			goto err;
		if (!isdec(p))
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // p
			goto err;
		if (!isdec(p))
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // salt
			goto err;
		if (hexlenl(p, &extra) != 64 || extra)
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // ciphertext
			goto err;
		if (hexlenl(p, &extra) > 128 || extra)
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // mac
			goto err;
		if (hexlenl(p, &extra) != 64 || extra)
			goto err;
	} else if (*p == 'w') {
		if ((p = strtokm(NULL, "*")) == NULL)   // encseed
			goto err;
		if (hexlenl(p, &extra) >= 2048 * 2 || extra)
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // ethaddr
			goto err;
		if (hexlenl(p, &extra) > 128 || extra)
			goto err;
		if ((p = strtokm(NULL, "*")) == NULL)   // bkp
			goto err;
		if (hexlenl(p, &extra) != 32 || extra)
			goto err;
	}

	MEM_FREE(keeptr);
	return 1;

err:
	MEM_FREE(keeptr);
	return 0;
}

void *ethereum_common_get_salt(char *ciphertext)
{
	char *ctcopy = strdup(ciphertext);
	char *keeptr = ctcopy;
	int i;
	char *p;
	static custom_salt *cur_salt;

	cur_salt = mem_calloc_tiny(sizeof(custom_salt), MEM_ALIGN_SIMD); // 64-bit alignment is required

	ctcopy += TAG_LENGTH;
	p = strtokm(ctcopy, "*");
	if (*p == 'p')
		cur_salt->type = 0; // PBKDF2
	else if (*p == 's')
		cur_salt->type = 1; // scrypt
	else if (*p == 'w')
		cur_salt->type = 2; // PBKDF2, presale wallet

	p = strtokm(NULL, "*");
	if (cur_salt->type == 0) {
		cur_salt->iterations = atoi(p);
		p = strtokm(NULL, "*");
	} else if (cur_salt->type == 1) {
		cur_salt->N = atoi(p);
		p = strtokm(NULL, "*");
		cur_salt->r = atoi(p);
		p = strtokm(NULL, "*");
		cur_salt->p = atoi(p);
		p = strtokm(NULL, "*");
	} else if (cur_salt->type == 2) {
		cur_salt->eslen = strlen(p) / 2;
		for (i = 0; i < cur_salt->eslen; i++)
			cur_salt->encseed[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}
	if (cur_salt->type == 0 || cur_salt->type == 1) {
		cur_salt->saltlen = strlen(p) / 2;
		for (i = 0; i < cur_salt->saltlen; i++)
			cur_salt->salt[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
		p = strtokm(NULL, "*");
		cur_salt->ctlen = strlen(p) / 2;
		for (i = 0; i < cur_salt->ctlen; i++)
			cur_salt->ct[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}

	MEM_FREE(keeptr);

	return (void *)cur_salt;
}

void *ethereum_get_binary(char *ciphertext)
{
	static union {
		unsigned char c[BINARY_SIZE];
		uint32_t dummy;
	} buf;
	unsigned char *out = buf.c;
	char *p;
	int i;
	p = strrchr(ciphertext, '*') + 1;
	for (i = 0; i < BINARY_SIZE; i++) {
		out[i] = (atoi16[ARCH_INDEX(*p)] << 4) | atoi16[ARCH_INDEX(p[1])];
		p += 2;
	}

	return out;
}

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

	if (cs->type == 0)
		return (unsigned int)cs->iterations;
	else if (cs->type == 1)
		return (unsigned int)cs->N;
	else
		return 2000;
}

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

	return cs->type;
}
