/*****************************************************************************
 * This software was written by Jim Fougeron jfoug AT cox dot net
 * in 2013-2016. No copyright is claimed, and the software is hereby
 * placed in the public domain. In case this attempt to disclaim
 * copyright and place the software in the public domain is deemed
 * null and void, then the software is Copyright (c) 2013-2016 Jim Fougeron
 * and it is hereby released to the general public under the following
 * terms:
 *
 * This software may be modified, redistributed, and used for any
 * purpose, in source and binary forms, with or without modification.
 *
 * Generic 'scriptable' hash cracker for JtR.  These are the 'larger' crypt
 * items. They have been separated from dynamic_fmt.c, and placed into this
 * stand alone file.  In this code, there are a lot of lines of code, but
 * the code is very cookie cutter.
 *
 *  NOTE the code was so cookie cutter, that in summer 2015, it was simply
 *       DELETED from the version control.  Now the code (THIS FILE), is
 *       auto-generated by the ./configure script.  All hashes will use
 *       the same prototype code, so once that code is written correctly
 *       then all hashes should work FINE. Also, if there is an optimization
 *       or extension found, then simply editing the prototype code and
 *       rerunning configure will get all hashes fixed, or updated.
 *
 *  NOTE All other sections of code that need 'hand edited' in the dynamic
 *       set of files, when adding a new hash type can be found by searching
 *       for this string:   LARGE_HASH_EDIT_POINT
 *
 ****************************************************************************/

#if AC_BUILT
#include "autoconfig.h"
#endif
#ifndef DYNAMIC_DISABLED

#include <stdint.h>

#include "openssl_local_overrides.h"

#include "arch.h"

#if defined(SIMD_COEF_32) && !ARCH_LITTLE_ENDIAN
	#undef SIMD_COEF_32
	#undef SIMD_COEF_64
	#undef SIMD_PARA_MD5
	#undef SIMD_PARA_MD4
	#undef SIMD_PARA_SHA1
	#undef SIMD_PARA_SHA256
	#undef SIMD_PARA_SHA512
	#define BITS ARCH_BITS_STR
#endif

#if !FAST_FORMATS_OMP
#ifdef _OPENMP
  #define FORCE_THREAD_MD5_body
#endif
#undef _OPENMP
#endif

#include "misc.h"
#include "common.h"
#include "formats.h"
#include "sha.h"
#include "sha2.h"
#include "md5.h"
#include "md4.h"
#include "dynamic.h"
#include "johnswap.h"
#include "simd-intrinsics.h"
#include "dynamic_types.h"

/*
 * NOTE!!!!  All hash types must use a CTX structure, and the 'final' function
 * must be the same syntax as the oSSL interface.  final(uchar *output, ctx *)
 * Hashes with this order reversed, need a #define here, to 'fix' their order
 */

#include "gost.h"
#define john_gost_final(a,b) john_gost_final(b,a)

#include "sph_ripemd.h"
#define sph_ripemd128_close(a,b) sph_ripemd128_close(b,a)
#define sph_ripemd160_close(a,b) sph_ripemd160_close(b,a)
#define sph_ripemd256_close(a,b) sph_ripemd256_close(b,a)
#define sph_ripemd320_close(a,b) sph_ripemd320_close(b,a)

#include "sph_tiger.h"
#define sph_tiger_close(a,b) sph_tiger_close(b,a)

#include "sph_haval.h"
#define sph_haval128_3_close(a,b) sph_haval128_3_close(b,a)
#define sph_haval128_4_close(a,b) sph_haval128_4_close(b,a)
#define sph_haval128_5_close(a,b) sph_haval128_5_close(b,a)
#define sph_haval160_3_close(a,b) sph_haval160_3_close(b,a)
#define sph_haval160_4_close(a,b) sph_haval160_4_close(b,a)
#define sph_haval160_5_close(a,b) sph_haval160_5_close(b,a)
#define sph_haval192_3_close(a,b) sph_haval192_3_close(b,a)
#define sph_haval192_4_close(a,b) sph_haval192_4_close(b,a)
#define sph_haval192_5_close(a,b) sph_haval192_5_close(b,a)
#define sph_haval224_3_close(a,b) sph_haval224_3_close(b,a)
#define sph_haval224_4_close(a,b) sph_haval224_4_close(b,a)
#define sph_haval224_5_close(a,b) sph_haval224_5_close(b,a)
#define sph_haval256_3_close(a,b) sph_haval256_3_close(b,a)
#define sph_haval256_4_close(a,b) sph_haval256_4_close(b,a)
#define sph_haval256_5_close(a,b) sph_haval256_5_close(b,a)

#include "sph_md2.h"
#define sph_md2_close(a,b) sph_md2_close(b,a)

#include "sph_panama.h"
#define sph_panama_close(a,b) sph_panama_close(b,a)

#include "sph_skein.h"
#define sph_skein224_close(a,b) sph_skein224_close(b,a)
#define sph_skein256_close(a,b) sph_skein256_close(b,a)
#define sph_skein384_close(a,b) sph_skein384_close(b,a)
#define sph_skein512_close(a,b) sph_skein512_close(b,a)

#if HAVE_LIBCRYPTO
#include <openssl/opensslv.h>
#endif
#if (AC_BUILT && HAVE_WHIRLPOOL) ||	  \
   (!AC_BUILT && OPENSSL_VERSION_NUMBER >= 0x10000000 && !HAVE_NO_SSL_WHIRLPOOL)
#include <openssl/whrlpool.h>
#else
#include "sph_whirlpool.h"
#define WHIRLPOOL_CTX             sph_whirlpool_context
#define WHIRLPOOL_Init(a)         sph_whirlpool_init(a)
#define WHIRLPOOL_Update(a,b,c)   sph_whirlpool(a,b,c)
#define WHIRLPOOL_Final(a,b)      sph_whirlpool_close(b,a)
#endif

#include "KeccakHash.h"
#define KECCAK_CTX                  Keccak_HashInstance
#define KECCAK_Update(a,b,c)        Keccak_HashUpdate(a,b,(c)*8)
#define KECCAK_Final(a,b)           Keccak_HashFinal(b,a)
#define KECCAK_224_Init(hash)       Keccak_HashInitialize(hash, 1152,  448, 224, 0x01)
#define KECCAK_256_Init(hash)       Keccak_HashInitialize(hash, 1088,  512, 256, 0x01)
#define KECCAK_384_Init(hash)       Keccak_HashInitialize(hash,  832,  768, 384, 0x01)
#define KECCAK_512_Init(hash)       Keccak_HashInitialize(hash,  576, 1024, 512, 0x01)
// FIPS202 complient
#define SHA3_224_Init(hash)         Keccak_HashInitialize(hash, 1152,  448, 224, 0x06)
#define SHA3_256_Init(hash)         Keccak_HashInitialize(hash, 1088,  512, 256, 0x06)
#define SHA3_384_Init(hash)         Keccak_HashInitialize(hash,  832,  768, 384, 0x06)
#define SHA3_512_Init(hash)         Keccak_HashInitialize(hash,  576, 1024, 512, 0x06)


#ifdef _OPENMP
#include <omp.h>
#endif


#if !defined (_DEBUG)
#define m_count m_Dynamic_Count
#endif
extern unsigned int m_count;

#define eLargeOut dyna_eLargeOut
extern eLargeOut_t *eLargeOut;
#define nLargeOff dyna_nLargeOff
extern unsigned *nLargeOff;

extern MD5_OUT *crypt_key_X86;
extern MD5_OUT *crypt_key2_X86;
extern MD5_IN *input_buf_X86;
extern MD5_IN *input_buf2_X86;
extern unsigned int *total_len_X86;
extern unsigned int *total_len2_X86;
extern BIG_HASH_OUT dynamic_BHO[4];

extern const char *dynamic_itoa16;

#if !defined (_DEBUG)
#define curdat Dynamic_curdat
#endif
extern private_subformat_data curdat;

#if !defined (_DEBUG)
#define itoa16_w2 __Dynamic_itoa_w2
#define itoa16_w2_u __Dynamic_itoa_w2_u
#define itoa16_w2_l __Dynamic_itoa_w2_l
#endif
extern unsigned short itoa16_w2_u[256], *itoa16_w2;

inline static void _eLargeOut_set(eLargeOut_t what, int tid)
{
	eLargeOut[tid] = what;
}

inline static int _eLargeOut_get(int tid)
{
	return eLargeOut[tid];
}

inline static void _nLargeOff_set(unsigned what, int tid)
{
	nLargeOff[tid] = what;
}

inline static int _nLargeOff_get(int tid)
{
	return nLargeOff[tid];
}

#if !defined (_OPENMP)
#define eLargeOut_set(what, tid)  _eLargeOut_set(what, 0)
#define eLargeOut_get(tid)        _eLargeOut_get(0)
#define nLargeOff_set(what, tid)  _nLargeOff_set(what, 0)
#define nLargeOff_get(tid)        _nLargeOff_get(0)
#else
#define eLargeOut_set(what, tid)  _eLargeOut_set(what, tid)
#define eLargeOut_get(tid)        _eLargeOut_get(tid)
#define nLargeOff_set(what, tid)  _nLargeOff_set(what, tid)
#define nLargeOff_get(tid)        _nLargeOff_get(tid)
#endif

/* These SIMPLE setter functions, change how the large hash output format is performed   */
/* Once set, it stays that way, until set a different way.  By DEFAULT (i.e. it is reset */
/* this way each time), when crypt_all is called, the large output is in eBase16 mode    */
// These MIGHT have problems in _OPENMP builds!!
void DynamicFunc__LargeHash_OUTMode_base16(DYNA_OMP_PARAMS)
{
	eLargeOut_set(eBase16,tid);
}

void DynamicFunc__LargeHash_OUTMode_base16u(DYNA_OMP_PARAMS)
{
	eLargeOut_set(eBase16u,tid);
}

void DynamicFunc__LargeHash_OUTMode_base64(DYNA_OMP_PARAMS)
{
	eLargeOut_set(eBase64,tid);
}

void DynamicFunc__LargeHash_OUTMode_base64c(DYNA_OMP_PARAMS)
{
	eLargeOut_set(eBase64c,tid);
}

void DynamicFunc__LargeHash_OUTMode_base64_nte(DYNA_OMP_PARAMS)
{
	eLargeOut_set(eBase64_nte,tid);
}

void DynamicFunc__LargeHash_OUTMode_raw(DYNA_OMP_PARAMS)
{
	eLargeOut_set(eBaseRaw,tid);
}


void DynamicFunc__LargeHash_set_offset_16(DYNA_OMP_PARAMS)
{
	nLargeOff_set(16,tid);
}
void DynamicFunc__LargeHash_set_offset_20(DYNA_OMP_PARAMS)
{
	nLargeOff_set(20,tid);
}
void DynamicFunc__LargeHash_set_offset_24(DYNA_OMP_PARAMS)
{
	nLargeOff_set(24,tid);
}
void DynamicFunc__LargeHash_set_offset_28(DYNA_OMP_PARAMS)
{
	nLargeOff_set(28,tid);
}
void DynamicFunc__LargeHash_set_offset_32(DYNA_OMP_PARAMS)
{
	nLargeOff_set(32,tid);
}
void DynamicFunc__LargeHash_set_offset_40(DYNA_OMP_PARAMS)
{
	nLargeOff_set(40,tid);
}
void DynamicFunc__LargeHash_set_offset_48(DYNA_OMP_PARAMS)
{
	nLargeOff_set(48,tid);
}
void DynamicFunc__LargeHash_set_offset_56(DYNA_OMP_PARAMS)
{
	nLargeOff_set(56,tid);
}
void DynamicFunc__LargeHash_set_offset_64(DYNA_OMP_PARAMS)
{
	nLargeOff_set(64,tid);
}
void DynamicFunc__LargeHash_set_offset_80(DYNA_OMP_PARAMS)
{
	nLargeOff_set(80,tid);
}
void DynamicFunc__LargeHash_set_offset_96(DYNA_OMP_PARAMS)
{
	nLargeOff_set(96,tid);
}
void DynamicFunc__LargeHash_set_offset_100(DYNA_OMP_PARAMS)
{
	nLargeOff_set(100,tid);
}
void DynamicFunc__LargeHash_set_offset_112(DYNA_OMP_PARAMS)
{
	nLargeOff_set(112,tid);
}
void DynamicFunc__LargeHash_set_offset_128(DYNA_OMP_PARAMS)
{
	nLargeOff_set(128,tid);
}
void DynamicFunc__LargeHash_set_offset_160(DYNA_OMP_PARAMS)
{
	nLargeOff_set(160,tid);
}
void DynamicFunc__LargeHash_set_offset_192(DYNA_OMP_PARAMS)
{
	nLargeOff_set(192,tid);
}
extern int get_dynamic_fmt_saltlen();
void DynamicFunc__LargeHash_set_offset_saltlen(DYNA_OMP_PARAMS)
{
	nLargeOff_set(get_dynamic_fmt_saltlen(),tid);
}
/******************************************************************************
 *****  These helper functions are used by all of the 'LARGE' hash functions.
 *****  These are used to convert an 'out' into the proper format, and writing
 *****  it to the buffer.  Currently we handle base-16, base-16u, base-64 and
 *****  raw buffer writing. These functions do not return any count of bytes
 *****  nor deal with things like overwrite/appending.  That has to be done in
 *****  the calling function.  The caller will get the pointers setup, then call
 *****  these helpers.  Then the caller will update any length values if needed
 *****  based upon what the output pointer was, and what was returned by these
 *****  helpers.  Doing things like this will reduce the size of the large hash
 *****  primitive functions.
 ******************************************************************************/
inline static unsigned char *hex_out_buf(unsigned char *cpi, unsigned char *cpo, int in_byte_cnt)
{
	unsigned int j;
	for (j = 0; j < in_byte_cnt; ++j) {
#if ARCH_ALLOWS_UNALIGNED
		*((unsigned short*)cpo) = itoa16_w2[*cpi++];
		cpo += 2;
#else
		*cpo++ = dynamic_itoa16[*cpi>>4];
		*cpo++ = dynamic_itoa16[*cpi&0xF];
		++cpi;
#endif
	}
	return cpo;
}

// NOTE, cpo must be at least in_byte_cnt*2 bytes of buffer
inline static unsigned char *hexu_out_buf(unsigned char *cpi, unsigned char *cpo, int in_byte_cnt)
{
	unsigned int j;
	for (j = 0; j < in_byte_cnt; ++j) {
#if ARCH_ALLOWS_UNALIGNED
		*((unsigned short*)cpo) = itoa16_w2_u[*cpi++];
		cpo += 2;
#else
		*cpo++ = itoa16u[*cpi>>4];
		*cpo++ = itoa16u[*cpi&0xF];
		++cpi;
#endif
	}
	return cpo;
}

// NOTE, cpo must be at least in_byte_cnt bytes of buffer
inline static unsigned char *raw_out_buf(unsigned char *cpi, unsigned char *cpo, int in_byte_cnt)
{
	unsigned int j;
#if ARCH_ALLOWS_UNALIGNED
	// note, all of these 'should' be even divisible by 4.  If not, then we need to rethink this logic.
	uint32_t *pi = (uint32_t*)cpi;
	uint32_t *po = (uint32_t*)cpo;
	in_byte_cnt>>=2;
	for (j = 0; j < in_byte_cnt; ++j)
		*po++ = *pi++;
	return (unsigned char*)po;
#else
	for (j = 0; j < in_byte_cnt; ++j)
		*cpo++ = *cpi++;
	return cpo;
#endif
}

// compatible 'standard' MIME base-64 encoding.
inline static unsigned char *base64_out_buf(unsigned char *cpi, unsigned char *cpo, int in_byte_cnt, int add_eq)
{
	static char *_itoa64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	while (in_byte_cnt > 2) {
		*cpo++ = _itoa64[(cpi[0] & 0xfc) >> 2];
		*cpo++ = _itoa64[((cpi[0] & 0x03) << 4) + ((cpi[1] & 0xf0) >> 4)];
		*cpo++ = _itoa64[((cpi[1] & 0x0f) << 2) + ((cpi[2] & 0xc0) >> 6)];
		*cpo++ = _itoa64[cpi[2] & 0x3f];
		cpi += 3;
		in_byte_cnt -= 3;
	}
	// easiest way is to simply have 2 'special' cases to handle these lengths
	if (in_byte_cnt==2)
	{
		*cpo++ = _itoa64[(cpi[0] & 0xfc) >> 2];
		*cpo++ = _itoa64[((cpi[0] & 0x03) << 4) + ((cpi[1] & 0xf0) >> 4)];
		*cpo++ = _itoa64[((cpi[1] & 0x0f) << 2)];
		if (add_eq) *cpo++ = '=';
	}
	if (in_byte_cnt==1)
	{
		*cpo++ = _itoa64[(cpi[0] & 0xfc) >> 2];
		*cpo++ = _itoa64[((cpi[0] & 0x03) << 4)];
		if (add_eq) { *cpo++ = '='; *cpo++ = '='; }
	}
	return cpo;
}

// compatible 'crypt' charset base-64 encoding.
inline static unsigned char *base64c_out_buf(unsigned char *cpi, unsigned char *cpo, int in_byte_cnt)
{
	static char *_itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

	while (in_byte_cnt > 2) {
		*cpo++ = _itoa64[(cpi[0] & 0xfc) >> 2];
		*cpo++ = _itoa64[((cpi[0] & 0x03) << 4) + ((cpi[1] & 0xf0) >> 4)];
		*cpo++ = _itoa64[((cpi[1] & 0x0f) << 2) + ((cpi[2] & 0xc0) >> 6)];
		*cpo++ = _itoa64[cpi[2] & 0x3f];
		cpi += 3;
		in_byte_cnt -= 3;
	}
	// easiest way is to simply have 2 'special' cases to handle these lengths
	if (in_byte_cnt==2)
	{
		*cpo++ = _itoa64[(cpi[0] & 0xfc) >> 2];
		*cpo++ = _itoa64[((cpi[0] & 0x03) << 4) + ((cpi[1] & 0xf0) >> 4)];
		*cpo++ = _itoa64[((cpi[1] & 0x0f) << 2)];
	}
	if (in_byte_cnt==1)
	{
		*cpo++ = _itoa64[(cpi[0] & 0xfc) >> 2];
		*cpo++ = _itoa64[((cpi[0] & 0x03) << 4)];
	}
	return cpo;
}

inline int large_hash_output(unsigned char *cpi, unsigned char *cpo, int in_byte_cnt, int tid)
{
	unsigned char *cpo2=cpo;
	switch(eLargeOut_get(tid)) {
		case eBase16:
			cpo2 = hex_out_buf(cpi, cpo, in_byte_cnt);
			break;
		case eBase16u:
			cpo2 = hexu_out_buf(cpi, cpo, in_byte_cnt);
			break;
		case eBase64:
			cpo2 = base64_out_buf(cpi, cpo, in_byte_cnt, 1);
			break;
		case eBase64_nte:
			cpo2 = base64_out_buf(cpi, cpo, in_byte_cnt, 0);
			break;
		case eBase64c:
			cpo2 = base64c_out_buf(cpi, cpo, in_byte_cnt);
			break;
		case eBaseRaw:
			cpo2 = raw_out_buf(cpi, cpo, in_byte_cnt);
			break;
		case eUNK:
		default:
			exit(fprintf(stderr, "Error, unknown 'output' state found in large_hash_output function, in %s\n", curdat.dynamic_WHICH_TYPE_SIG));
	}
	return cpo2-cpo;
}

#if SIMD_COEF_32
inline static uint32_t Do_FixBufferLen32(unsigned char *input_buf, int total_len, int BE_HASH)
{
	uint32_t *p;
	unsigned char *cp;
	unsigned int i;
	uint32_t ret = (total_len / 64) + 1;

	if (total_len % 64 > 55)
		++ret;
	cp = &(input_buf[total_len]);
	i = total_len;
	// first, get us to an even 32 bit boundary.
	while (i&3) {
		*cp++ = 0;
		++i;
	}
	// now switch to uint_32's
	p = (uint32_t *)cp;
	// this is how many 32 bit words max we will clean.
	i = ((ret<<6)-i)/4-2;
	while (i--) {
		*p++ = 0;
		if (!p[0] && !p[1])
		break;
	}
	input_buf[total_len] = 0x80;
	p = (uint32_t *)input_buf;
	if (BE_HASH) {
		p[(ret*16)-2] = 0;
		p[(ret*16)-1] = JOHNSWAP(total_len<<3);
	} else {
		p[(ret*16)-2] = (total_len<<3);
		p[(ret*16)-1] = 0;
	}
	return ret;
}

inline static uint32_t Do_FixBufferLen64(unsigned char *input_buf, int total_len, int BE_HASH)
{
	uint64_t *p;
	unsigned char *cp;
	unsigned int i;
	unsigned int ret = (total_len / 128) + 1;

	if (total_len % 128 > 111)
		++ret;
	cp = &(input_buf[total_len]);
	i = total_len;
	// first, get us to an even 64 bit boundary.
	while (i&7) {
		*cp++ = 0;
		++i;
	}
	// now switch to uint_64's
	p = (uint64_t *)cp;
	// this is how many 64 bit words max we will clean.
	i = ((ret<<7)-i)/8-2;
	while (i--) {
		*p++ = 0;
		if (!p[0] && !p[1])
			break;
	}
	if ( ( ((unsigned char*)p)-input_buf) < 56) {
		p = &(((uint64_t *)input_buf)[7]);
		i = 22; // 256 bytes - 64 bytes (for first 64 byte MD buffer) / 8 bytes per uint64_t - 2 (-2 is last 16 bytes of 256 buffer)
		do {
			*p++ = 0;
			if (!p[0] && !p[1])
				break;
		} while (i--);
	} else if ( ( ((unsigned char*)p)-input_buf) > 111 && ( ((unsigned char*)p)-input_buf) < 184) {
		p = &(((uint64_t *)input_buf)[23]);
		i = 6; // 256 bytes - 192 bytes (for first 3-32 byte MD buffers) / 8 bytes per uint64_t - 2 (-2 is last 16 bytes of 256 buffer)
		do {
			*p++ = 0;
			if (!p[0] && !p[1])
				break;
		} while (i--);
	}
	input_buf[total_len] = 0x80;
	p = (uint64_t *)input_buf;

	if (BE_HASH) {
		p[(ret*16)-2] = 0;
		p[(ret*16)-1] = JOHNSWAP64(total_len<<3);
	} else {
		p[(ret*16)-2] = (total_len<<3);
		p[(ret*16)-1] = 0;
	}

	return ret;
}
#endif

#ifdef _OPENMP
#define PRELIM_W_TID   uint32_t i=first, til=last
#define PRELIM_NO_TID  uint32_t i=first, til=last
#else
#define PRELIM_W_TID   uint32_t i=0, til=m_count, tid=0
#define PRELIM_NO_TID  uint32_t i=0, til=m_count
#endif

/****************************************************************************
 ****************************************************************************
 ** NOTE, all code after this point should NEVER be hand edited.           **
 ** if there are bugs found, then the code in dynamic_big_crypt_hash.cin   **
 ** is what needs to be fixed, and then the build procedure 'may' also     **
 ** need to be updated.  A perl script, and the dynamic_big_crypt_hash.cin **
 ** is used to generate all the code after this comment!                   **
 *****************************************************************************
 ****************************************************************************/
