/* rar2john utility for RAR 3.x files, written in April of 2011 by Dhiru Kholia for GSoC.
 * rar2john processes input RAR files into a format suitable for use with JtR.
 *
 * This software is Copyright © 2011, Dhiru Kholia <dhiru.kholia at gmail.com>,
 * 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.
 *
 * Huge thanks to Marc Bevand <m.bevand (at) gmail.com> for releasing unrarhp
 * (http://www.zorinaq.com/unrarhp/) and documenting the RAR encryption scheme.
 * This patch is made possible by unrarhp's documentation.
 *
 * Usage:
 *
 * 1. Run rar2john on rar file(s) as "rar2john [rar files]".
 *    Output is written to standard output.
 * 2. Run JtR on the output generated by rar2john as "john [output file]".
 *
 * Output Line Format:
 *
 * For type = 0 for files encrypted with "rar -hp ..." option
 * filename:$rar3$*type*hex(salt)*:hex(partial-file-contents)
 *
 * For type = 1 for files encrypted with "rar -p ..." option
 * filename:$rar3$*type*hex(salt)*hex(crc):PACK_SIZE:UNP_SIZE:filename:file-offset-for-ciphertext-data
 *
 * Note that the PACK_SIZE can be huge which implies that it can't be
 * stored in type 0's compact "rardump" format */

#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#include "misc.h"
#include "common.h"
#include "arch.h"
#include "params.h"
#include "crc32.h"

static void process_file(const char *filename)
{
	FILE *fp;
	unsigned char marker_block[7];
	unsigned char archive_header_block[13];
	unsigned char file_header_block[40];
	int i, count, type;

	if (!(fp = fopen(filename, "rb"))) {
		fprintf(stderr, "! %s : %s\n", filename, strerror(errno));
		return;
	}
	/* marker block */
	memset(marker_block, 0, 7);
	count = fread(marker_block, 7, 1, fp);
	assert(count == 1);
	if (memcmp(marker_block, "\x52\x61\x72\x21\x1a\x07\x00", 7)) {
		fprintf(stderr, "! %s : Not a RAR file\n", filename);
		fclose(fp);
		return;
	}
	/* archive header block */
	count = fread(archive_header_block, 13, 1, fp);
	assert(count == 1);
	assert(archive_header_block[2] == 0x73);
	/* find encryption mode used (called type in output line format) */
	uint16_t archive_header_head_flags =
	    archive_header_block[4] << 8 | archive_header_block[3];
	if (archive_header_head_flags & 0x0080) {	/* file header block is encrypted */
		type = 0;	/* RAR file was created using -hp flag */
	} else
		type = 1;
	/* file header block */
	count = fread(file_header_block, 32, 1, fp);
	assert(count == 1);
	if (type == 1)
		assert(file_header_block[2] == 0x74);
	uint16_t file_header_head_flags =
	    file_header_block[4] << 8 | file_header_block[3];
	/* if type = 1, check if encryption is being used? */
	if (type == 1 && !(file_header_head_flags & 0x04)) {
		fprintf(stderr, "! %s : RAR file is not encrypted\n",
		    filename);
		fclose(fp);
		return;
	}

	/* process -hp mode files */
	if (type == 0) {	/* use Marc's end-of-archive block decrypt trick */
		printf("%s:$rar3$*%d*", filename, type);
		fseek(fp, -24, SEEK_END);
		unsigned char buf[24];
		count = fread(buf, 24, 1, fp);
		assert(count == 1);
		for (i = 0; i < 8; i++) { /* salt */
			printf("%c%c", itoa16[ARCH_INDEX(buf[i] >> 4)],
			    itoa16[ARCH_INDEX(buf[i] & 0x0f)]);
		}
		printf("*");
		for (i = 8; i < 24; i++) {  /* encrypted block with known plaintext */
			printf("%c%c", itoa16[ARCH_INDEX(buf[i] >> 4)],
			    itoa16[ARCH_INDEX(buf[i] & 0x0f)]);
		}
		printf("\n");
	} else {    /* TODO: process -p mode files */
		if (!(file_header_head_flags & 0x8000)) {
			fprintf(stderr, "bailing out ...\n");
			return;
		}
		uint16_t file_header_head_size =
		    file_header_block[6] << 8 | file_header_block[5];
		int file_header_pack_size;
		memcpy(&file_header_pack_size, file_header_block + 7, 4);
		int file_header_unp_size;
		memcpy(&file_header_unp_size, file_header_block + 11, 4);
		fprintf(stderr, "HEAD_SIZE : %d, PACK_SIZE : %d, UNP_SIZE : %d\n",
		    file_header_head_size, file_header_pack_size,
		    file_header_unp_size);

		/* calculate EXT_TIME size */
		int EXT_TIME_SIZE = file_header_head_size - 32;

		char rejbuf[32];
		if (file_header_head_flags & 0x100) {
			fprintf(stderr, "! HIGH_PACK_SIZE present\n");
			count = fread(rejbuf, 4, 1, fp);
			assert(count == 1);
			EXT_TIME_SIZE -= 4;
		}
		if (file_header_head_flags & 0x100) {
			fprintf(stderr, "! HIGH_UNP_SIZE present\n");
			count = fread(rejbuf, 4, 1, fp);
			assert(count == 1);
			EXT_TIME_SIZE -= 4;
		}
		/* file name processing */
		uint16_t file_name_size =
		    file_header_block[27] << 8 | file_header_block[26];
		fprintf(stderr, "file name size : %d bytes\n", file_name_size);
		unsigned char file_name[128];
		count = fread(file_name, file_name_size, 1, fp);
		assert(count == 1);
		file_name[file_name_size] = 0;
		fprintf(stderr, "file name : %s\n", file_name);
		EXT_TIME_SIZE -= file_name_size;
		/* SALT processing */
		unsigned char SALT[8];
		if (file_header_head_flags & 0x400) {
			EXT_TIME_SIZE -= 8;
			count = fread(SALT, 8, 1, fp);
			assert(count == 1);
		}
		/* EXT_TIME processing */
		if (file_header_head_flags & 0x1000) {
			fprintf(stderr, "! EXT_TIME present with size %d\n",
			    EXT_TIME_SIZE);
			fread(rejbuf, EXT_TIME_SIZE, 1, fp);
			assert(count == 1);
		}
		/* process encrypted data of size "file_header_pack_size" */
		printf("%s:$rar3$*%d*", filename, type);
		for (i = 0; i < 8; i++) { /* encode SALT */
			printf("%c%c", itoa16[ARCH_INDEX(SALT[i] >> 4)],
			    itoa16[ARCH_INDEX(SALT[i] & 0x0f)]);
		}
		printf("*");
		unsigned char FILE_CRC[4];
		memcpy(FILE_CRC, file_header_block + 16, 4);
		for (i = 0; i < 4; i++) { /* encode FILE_CRC */
			printf("%c%c", itoa16[ARCH_INDEX(FILE_CRC[i] >> 4)],
			    itoa16[ARCH_INDEX(FILE_CRC[i] & 0x0f)]);
		}
		/* fp is at ciphertext location */
		long pos = ftell(fp);
		printf("*%d*%d*%s*%ld\n",file_header_pack_size, file_header_unp_size, filename, pos);
	}
	fclose(fp);
}

int rar2john(int argc, char **argv)
{
	int i;

	if (argc < 2) {
		puts("Usage: rar2john [rar files]");
		return 0;
	}
	for (i = 1; i < argc; i++)
		process_file(argv[i]);

	return 0;
}
