/* zip2john processes input ZIP 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.
 *
 * References:
 *
 * 1. http://www.winzip.com/aes_info.htm
 * 2. http://www.winzip.com/aes_tips.htm
 * 4. ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
 * 5. Nathan Moinvaziri's work in extending minizip to support AES.
 * 6. http://oldhome.schmorp.de/marc/fcrackzip.html (coding hints)
 * 7. http://www.pkware.com/documents/casestudies/APPNOTE.TXT
 * 8. http://gladman.plushost.co.uk/oldsite/cryptography_technology/fileencrypt/index.php
 *   (borrowed files have "gladman_" prepended to them)
 *
 * Usage:
 *
 * 1. Run zip2john on zip file(s) as "zip2john [zip files]".
 *    Output is written to standard output.
 * 2. Run JtR on the output generated by zip2john as "john [output file]".
 *
 * Output Line Format:
 *
 * For type = 0, for ZIP files encrypted using AES
 * filename:$zip$*type*hex(CRC)*encryption_strength*hex(salt)*hex(password_verfication_value):hex(authentication_code)
 *
 * For type = 1, for ZIP files encrypted using old encryption (TODO)
 * filename:$zip$*type*...  */

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

/* helper functions for byte order conversions, header values are stored
 * in little-endian byte order */
static uint32_t fget32(FILE * fp)
{
	uint32_t v = fgetc(fp);
	v |= fgetc(fp) << 8;
	v |= fgetc(fp) << 16;
	v |= fgetc(fp) << 24;
	return v;
}

static uint16_t fget16(FILE * fp)
{
	uint32_t v = fgetc(fp);
	v |= fgetc(fp) << 8;
	return v;
}

static void process_file(const char *fname)
{
	unsigned char filename[1024];
	FILE *fp;
	int i;

	if (!(fp = fopen(fname, "rb"))) {
		fprintf(stderr, "! %s : %s\n", fname, strerror(errno));
		return;
	}

	while (!feof(fp)) {
		uint32_t id = fget32(fp);

		if (id == 0x04034b50UL) {	/* local header */
			uint16_t version = fget16(fp);
			uint16_t flags = fget16(fp);
			uint16_t compression_method = fget16(fp);
			uint16_t lastmod_time = fget16(fp);
			uint16_t lastmod_date = fget16(fp);
			uint32_t crc = fget32(fp);
			uint32_t compressed_size = fget32(fp);
			uint32_t uncompressed_size = fget32(fp);
			uint16_t filename_length = fget16(fp);
			uint16_t extrafield_length = fget16(fp);
			/* unused variables */
			(void) version;
			(void) lastmod_time;
			(void) lastmod_date;
			(void) crc;
			(void) uncompressed_size;

			fread(filename, 1, filename_length, fp);
			filename[filename_length] = 0;

			if (compression_method == 99) {	/* AES encryption */
				fprintf(stderr,
				    "%s->%s is using AES encryption, extrafield_length is %d\n",
				    fname, filename, extrafield_length);
				uint16_t efh_id = fget16(fp);
				uint16_t efh_datasize = fget16(fp);
				uint16_t efh_vendor_version = fget16(fp);
				uint16_t efh_vendor_id = fget16(fp);
				char efh_aes_strength = fgetc(fp);
				uint16_t actual_compression_method =
				    fget16(fp);

				/* unused variables */
				(void) efh_id;
				(void) efh_datasize;
				(void) efh_vendor_version;
				(void) efh_vendor_id;
				(void) efh_aes_strength;
				(void) actual_compression_method;

				printf("%s:$zip$*0*%d*", fname,
				    efh_aes_strength);
				unsigned char salt[16];
				int n = 0;
				switch (efh_aes_strength) {
				case 1:
					n = 8;
					fread(salt, 1, n, fp);
					break;
				case 2:
					n = 12;
					fread(salt, 1, n, fp);
					break;
				case 3:
					n = 16;
					fread(salt, 1, n, fp);
					break;

				}
				for (i = 0; i < n; i++) {
					printf("%c%c",
					    itoa16[ARCH_INDEX(salt[i] >> 4)],
					    itoa16[ARCH_INDEX(salt[i] &
						    0x0f)]);
				}
				uint16_t password_verification_value =
				    fget16(fp);
				unsigned char *p = (unsigned char *)
				    &password_verification_value;
				printf("*");
				for (i = 0; i < 2; i++) {
					printf("%c%c",
					    itoa16[ARCH_INDEX(p[i] >> 4)],
					    itoa16[ARCH_INDEX(p[i] & 0x0f)]);
				}
				printf("\n");
				fseek(fp, 10, SEEK_CUR);

			} else if (flags & 1) {	/* old encryption */
				printf("%s is using old encryption!\n",
				    filename);
			} else {
				printf("%s->%s is not encrypted!\n", fname,
				    filename);
				fseek(fp, extrafield_length, SEEK_CUR);
				fseek(fp, compressed_size, SEEK_CUR);
			}
		} else if (id == 0x08074b50UL) {	/* data descriptor */
			fseek(fp, 12, SEEK_CUR);
		} else if (id == 0x02014b50UL || id == 0x06054b50UL) {	/* central directory structures */
			goto cleanup;
		}
	}

cleanup:
	fclose(fp);
}

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

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

	return 0;
}
