/*
 * Common code for the TGS-REP etype 17/18 formats.
 *
 * This software is Copyright (c) 2023 magnum,
 * and it is hereby released to the general public under the following terms:
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted.
 */

#include <ctype.h>

#include "formats.h"
#include "arch.h"
#include "misc.h"
#include "common.h"
#include "unicode.h"
#include "krb5_tgsrep_common.h"

/*
 * Formats are:
 * $krb5tgs$17$salt$checksum$edata2             // 5
 * $krb5tgs$18$salt$checksum$edata2
 * $krb5tgs$17$user$realm$checksum$edata2       // 6
 * $krb5tgs$18$user$realm$checksum$edata2
 * $krb5tgs$17$user$realm$*spn*$checksum$edata2 // 7
 * $krb5tgs$18$user$realm$*spn*$checksum$edata2
 *
 * checksum == edata1
 * salt == uc(realm).user
 */
struct fmt_tests krb5_tgsrep_tests[] = {
	{"$krb5tgs$17$user$realm$ae8434177efd09be5bc2eff8$90b4ce5b266821adc26c64f71958a475cf9348fce65096190be04f8430c4e0d554c86dd7ad29c275f9e8f15d2dab4565a3d6e21e449dc2f88e52ea0402c7170ba74f4af037c5d7f8db6d53018a564ab590fc23aa1134788bcc4a55f69ec13c0a083291a96b41bffb978f5a160b7edc828382d11aacd89b5a1bfa710b0e591b190bff9062eace4d26187777db358e70efd26df9c9312dbeef20b1ee0d823d4e71b8f1d00d91ea017459c27c32dc20e451ea6278be63cdd512ce656357c942b95438228e", "hashcat"},
	{"$krb5tgs$18$user$realm$8efd91bb01cc69dd07e46009$7352410d6aafd72c64972a66058b02aa1c28ac580ba41137d5a170467f06f17faf5dfb3f95ecf4fad74821fdc7e63a3195573f45f962f86942cb24255e544ad8d05178d560f683a3f59ce94e82c8e724a3af0160be549b472dd83e6b80733ad349973885e9082617294c6cbbea92349671883eaf068d7f5dcfc0405d97fda27435082b82b24f3be27f06c19354bf32066933312c770424eb6143674756243c1bde78ee3294792dcc49008a1b54f32ec5d5695f899946d42a67ce2fb1c227cb1d2004c0", "hashcat"},
	// extracted from the network traffic between 2016 AD and 2016 client, TGS-REP traffic generated by https://github.com/nidem/kerberoast
	{"$krb5tgs$18$EXAMPLE.NETlulu$f72d514cff17d1a7f6213816$95613ea2ea4ef8080d9b5957b09a71a1852454c71a1e52bcd246bc118ae2a539f7f919decba1fccad911c85772f4be450f68804e7efe64c72b78a0fbdad4b6fdce837c0d3acd56a723c702994dbf5a51a38237c263e6c0ca5d691de478a868e0372902d069358accf2f84b1f4f3ca2ffe15c40af4e654292aa52759261e87089ac0a9daaf13dd02c0cd751a8303befecf05add1196b55eea7e86cfa95e4c1f92e5640610c44013ce5e7587c9d3c697523b9f2d3a54ad0dda841e7d07a7691ad369657bc4cc12ec2f514a91ab67b5ec0d2f70a15241cc7d988bb8e515da283fbc493a028a31ace67ba610e43788a611d2281a008b217eb935ae7f36dd75fa43dd8d5f9dd657f1073365076b81ae59faf4189284b7bd883b64be34e163904815a43122d175a52647205f9a031d8722608106fecdb7f495ecaa580e6e40063bfd8e94b1240ffb19f55c710ecf4fef135477672c2e389ff6a29b08de45d14e66f321a7d77106c431882c4b5a444640454166d2d7631aa24f7afefb69180b2e51d32f536aebbea3481dcf0329c9618916a823d4aed8b909b573c9efe0847d5f772abe7790a5a57d9ac846da162a198190f60a27407bfbe8ea38988c9f4b258ed7374d1a7dd75aad51797a6d92e1045b85cba72294466a377a91e4271538bcbef67961691671be979b3cc4702743d3ce2839c03108fca779f6576e058eb3e67429817ae21574665a4aad1988e374d7142998502a7ea00db98d0bb102fe56ccd3741e0cddff5d0ce193facf6916995741ebbc77e4034b083d900dd5aa4ed7fc1713579ffb23563372703e8f2dedffca2d0553e1082379c7612809f4c5ec0729e5bc7fea7bb223c43249f98f0c59d1f3bc4d71460568ed4d6ebea7582bcd0a073bea283276917665a49468e9211e7a780c9809dc67d50ded82f23cef606f6110428ac43361dbd79afb608ed1407e5ca7014ccaa060461d180c73c25259fac23e4af825d868a6f5813da9fdbcf94f3d63b9726b92ff6d15d3f00389bd6d0b035d1b1ca868b96f842fa4850187c76a4e5828c7951ab9a2bb9bb4f0594e3233fa09b755e8e5da73133f3bd00d0a7562359395ab7f3aeb569dbf20a7cd458813754fbd5438049896aac9b75f7e361c64a232ac4b54d95b5d153f86c8e3b9250e5c218797e3bd73e83d3cec0030475d6ffc253e365eb0f2c1920e4b3c276b2291615aeb196ed45f0d89a0db02434a4e219c665f6fa92631576322e0770debc2fee762c76646a654c0e4f77afdc0d714f2412610abd936ecc80f9aff2ed4f736f4e9b4c0dd1750b50301be533475a0144b1aa78967fbded21b1021b56d3692212080356e6c668b8098b776dd24fa886a3cc70826254ecd39923d1bceeb4686aba928337fb15d1e76a033166d648f30843efd25e629612f9a5b0146b0f348c0bd9c", "openwall@123"},
	{NULL}
};

static size_t count_sep(char *ciphertext, char sep)
{
	size_t count = 0;

	while (*ciphertext)
		if (*ciphertext++ == sep)
			count++;

	return count;
}

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

	if (strncmp(ciphertext, FORMAT_TAG, FORMAT_TAG_LEN))
		return 0;

	int num_seps = count_sep(ciphertext, '$');

	if (num_seps < 4 || num_seps > 7)
		return 0;

	ciphertext += FORMAT_TAG_LEN;
	ctcopy = xstrdup(ciphertext);
	keeptr = ctcopy;

	if (strncmp(ctcopy, "17$", ETYPE_TAG_LEN) && strncmp(ctcopy, "18$", ETYPE_TAG_LEN))
		goto err;
	ctcopy += ETYPE_TAG_LEN;

	char *u;
	if ((u = strtokm(ctcopy, "$")) == NULL) // user (or complete salt, if there's no realm field)
		goto err;
	if (strlen(u) > sizeof(((krb5tgsrep_salt*)NULL)->salt))
		goto err;
	if (num_seps > 5) {
		if ((p = strtokm(NULL, "$")) == NULL) // realm
			goto err;
		if (strlen(u) + strlen(p) > sizeof(((krb5tgsrep_salt*)NULL)->salt))
			goto err;
	}
	if (num_seps > 6)
		if ((p = strtokm(NULL, "$")) == NULL) // spn (not used)
			goto err;
	if (((p = strtokm(NULL, "$")) == NULL)) // checksum/edata1
		goto err;
	if (!ishex(p) || hexlen(p, &extra) != 24 || extra)
		goto err;
	if ((p = strtokm(NULL, "$")) == NULL) // edata2
		goto err;
	if (!ishex(p) || hexlen(p, &extra) < 64 || extra)
		goto err;
	MEM_FREE(keeptr);
	return 1;

err:
	MEM_FREE(keeptr);
	return 0;
}

void *krb5_tgsrep_get_salt(char *ciphertext)
{
	static krb5tgsrep_salt cs;
	static void *ptr;
	int i;
	char *p, *ctcopy, *keeptr;
	int num_seps = count_sep(ciphertext, '$');

	ctcopy = xstrdup(ciphertext + FORMAT_TAG_LEN);
	keeptr = ctcopy;
	memset(&cs, 0, sizeof(cs));
	cs.edata2 = NULL;

	p = strtokm(ctcopy, "$");
	cs.etype = atoi(p);

	char *user, *realm;

	// salt
	user = strtokm(NULL, "$");
	if (num_seps > 5)
		realm = enc_strupper(strtokm(NULL, "$"));
	else
		realm = "";
	snprintf(cs.salt, sizeof(cs.salt), "%s%s", realm, user);

	// spn (not used)
	if (num_seps > 6)
		p = strtokm(NULL, "$");

	// checksum
	p = strtokm(NULL, "$");
	for (i = 0; i < 12; i++) {
		cs.edata1[i] =
			atoi16[ARCH_INDEX(p[i * 2])] * 16 +
			atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}

	// edata2
	p = strtokm(NULL, "$");
	cs.edata2len = strlen(p) / 2;
	cs.edata2 = (unsigned char*)mem_calloc_tiny(cs.edata2len, sizeof(char));
	for (i = 0; i < cs.edata2len; i++) {
		cs.edata2[i] =
			atoi16[ARCH_INDEX(p[i * 2])] * 16 +
			atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}

	MEM_FREE(keeptr);

	cs.dsalt.salt_cmp_offset = SALT_CMP_OFF(krb5tgsrep_salt, edata1);
	cs.dsalt.salt_cmp_size = SALT_CMP_SIZE(krb5tgsrep_salt, edata1, edata2len, 0);
	cs.dsalt.salt_alloc_needs_free = 0;

	ptr = mem_alloc_tiny(sizeof(krb5tgsrep_salt), MEM_ALIGN_WORD);
	memcpy(ptr, &cs, sizeof(krb5tgsrep_salt));

	return (void *) &ptr;
}

unsigned int krb5_tgsrep_etype(void *salt)
{
	return (*(krb5tgsrep_salt**)salt)->etype;
}
