/*
 * Crack the restriction PIN-code for your iDevice. Developed with the
 * information found here:
 *
 *	https://nbalkota.wordpress.com/2014/04/05/recover-your-forgotten-ios-7-restrictions-pin-code/
 *
 * Requires OpenSSL or compatible API. Download raw C-code and compile
 * it with something like:
 *
 *	cc -march=native -O2 -o ipin ipin.c -lcrypto
 *
 * If your compiler has OpenMP support, enable it -- the program will
 * use your multiple processors.
 *
 *	Copyright (c) 2016, 2017 by Mikhail Teterin.
 *	All rights reserved in perpetuity.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *	1. Owners of clothing or other paraphernalia displaying the
 *	likeness of Che Guevara are not allowed to use this software
 *	until they dispose of any and all such items in their possession.
 *
 *	2. Redistributions of source code must retain the above copyright
 *	notice, this list of conditions, and the following disclaimer.
 *
 *	3. Redistributions in binary form must reproduce the above
 *	copyright notice, this list of conditions and the following
 *	disclaimer in the documentation and/or other materials
 *	provided with the distribution.
 *
 *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
 *	CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *	OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <err.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>

static int
usage(const char *prog, int code)
{
	fprintf(stderr, "Usage:\n\t%s [-i iterations] [-v] key salt\n"
	    "\nThe BASE64-encoded key and salt are usually found in the "
	    "file named\n\n"
	    "\t398bc9c2aeeab4cb0c12ada0f52eea12cf14f40b\n\n"
	    "under the backup-directory.\n", prog);
	exit(code);
}

/*
 * Based on example from: https://gist.github.com/barrysteyn/7308212
 */
static int
base64_decode(/* const */ char *encoded, void **out)
{
	BIO	*bio, *b64;
	int	 inlen, outlen;

	inlen = strlen(encoded);
	*out = malloc(inlen);
	if (*out == NULL)
		err(EX_SOFTWARE, "maloc failed");
	bio = BIO_new_mem_buf(encoded, inlen);
	b64 = BIO_new(BIO_f_base64());
	bio = BIO_push(b64, bio);
	BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
	outlen = BIO_read(bio, *out, inlen);
	BIO_free_all(bio);

	return outlen;
}

int
main(int argc, char *argv[])
{
	void *key, *salt;
	int keylen, saltlen, answer, iterations = 1000, verbosity = 1;
	char *p;
	volatile int found = -1;

	while ((answer = getopt(argc, argv, "i:qvh")) != -1) {
		switch (answer) {
		case 'i':
			iterations = strtol(optarg, &p, 0);
			if (p[0] != '\0' || iterations <= 0)
				err(EX_DATAERR, "%s: invalid iteration-count",
				    optarg);
			break;
		case 'v':
			verbosity++;
			break;
		case 'q':
			verbosity = 0;
			break;
		case 'h':
			usage(argv[0], EX_OK);
		default:
			usage(argv[0], EX_USAGE);
		}
	}

	if (argc != optind + 2)
		usage(argv[0], EX_USAGE);

	if (verbosity > 0 && iterations != 1000)
		warnx("Non-default iteration-count (%d), "
		    "you better know, what you are doing.",
		    iterations);

	keylen = base64_decode(argv[optind], &key);
	saltlen = base64_decode(argv[optind + 1], &salt);
	if (keylen <= 0 || saltlen <= 0)
		errx(EX_DATAERR, "BASE64-decoding of supplied strings failed");

	if (verbosity > 0 && (keylen != 20 || saltlen != 4))
		warnx("Key is supposed to be 20-bytes long and salt -- 4 bytes.");

#ifdef _OPENMP
#pragma omp parallel for shared(found)
#endif
	for (answer = 0; answer < 10000; answer++) {
		unsigned char out[keylen];
		char buf[5];

#ifdef _OPENMP
		if (found != -1)
			continue;
#endif

		sprintf(buf, "%04d", answer);
		if (PKCS5_PBKDF2_HMAC_SHA1(buf, 4, salt, saltlen, iterations,
		    keylen, out) != 1)
			errx(EX_SOFTWARE, "PKCS5_PBKDF2_HMAC_SHA1 failed on "
			    "attempt %4s", buf);
		if (memcmp(key, out, keylen) == 0) {
			found = answer;
#ifndef _OPENMP	/* Can't break from parallelized loop, when using OpenMP */
			break;
#endif
		}
	}
	if (found == -1) {
		if (verbosity > 0)
			errx(1, "Could not find PIN -- are the supplied "
			    "arguments correct?");
		else
			exit(1);
	}
	printf("%04d\n", found);
	return 0;
}