// File: /workspace/github_c_makefile_star2_O2/mjosaarinen[P]brutus/crypto_aead/paeq128tnm/aesni/encrypt.cpp
// Function: GenerateNonceOpt(unsigned char*, unsigned char const*, unsigned long long, unsigned char const*, unsigned long long, unsigned char const*)
int GenerateNonceOpt(unsigned char* output, const unsigned char *m, unsigned long long mlen,
	const unsigned char *ad, unsigned long long adlen,
	const unsigned char *k)
	//Generate a nonce as a hash of the plaintext & AD by the AESQ-sponge function
{
	//Does not work for keys longer than 192 bits
	if (CRYPTO_KEYBYTES >24)
	{
		memset(output, 0, CRYPTO_NPUBBYTES);
		return -1;
	}

	__m128i State[4];  //sponge internal state
	__m128i StateOut[4];  //sponge internal state
	State[0] = State[1] = State[2] = State[3] = _mm_set_epi64x(0, 0);

	__m128i Injection[2]; //We inject into the first 32 bytes


	//Plaintext length -- 12 bytes and AD length - 12 bytes
	Injection[0] = _mm_set_epi64x(adlen << (CRYPTO_LENGTH*8-64), mlen);
	Injection[1] = _mm_set_epi64x(0, adlen >> (CRYPTO_LENGTH * 8 - 64));


	//Key and nonce length
	__m128i tmp = _mm_set_epi64x(CRYPTO_KEYBYTES + (CRYPTO_NPUBBYTES << 8), 0);
	Injection[1] = _mm_xor_si128(Injection[1], tmp);

	//Add key
	__m128i key = _mm_loadu_si128((__m128i*)k);
	Injection[1] = _mm_xor_si128(Injection[1], _mm_slli_si128(key, 2*CRYPTO_LENGTH+2-16)); //6 key bytes inserted

	//Iteration
	State[0] = _mm_xor_si128(State[0], Injection[0]);
	State[1] = _mm_xor_si128(State[1], Injection[1]);
	FPermAsm(State, StateOut);


	//Main loop
	unsigned state_index = 0;
	unsigned long long message_index = 0;
	unsigned long long ad_index = 0;
	unsigned char buf[32];

	//Remaining key bytes
	Injection[0] = _mm_srli_si128(key, CRYPTO_KEYBYTES - (2 * CRYPTO_LENGTH + 2 - 16));
	key = _mm_xor_si128(key, key);
	state_index = 2 * CRYPTO_LENGTH + 2 - 16;
	State[0] = _mm_xor_si128(StateOut[0], Injection[0]);
	State[1] = StateOut[1];
	State[2] = StateOut[2];
	State[3] = StateOut[3];

	//Injecting plaintext
	while (mlen >message_index) //we can load 32-byte chunks
	{
		if ((state_index == 0) && (mlen >= message_index + 32))
		{
			Injection[0] = _mm_loadu_si128((__m128i*)(m + message_index));
			Injection[1] = _mm_loadu_si128((__m128i*)(m + message_index+16));
			state_index = 32;
			message_index += 32;
		}
		else
		{
			memset(buf, 0, 32);
			if ((32 - state_index) <= (mlen - message_index))
			{
				memcpy(buf + state_index, m + message_index, 32 - state_index);
				message_index += 32-state_index;
				state_index = 32;

			}
			else
			{
				memcpy(buf + state_index, m + message_index, mlen - message_index);
				state_index += mlen - message_index;
				message_index = mlen;
			}
			Injection[0] = _mm_loadu_si128((__m128i*)buf);
			Injection[1] = _mm_loadu_si128((__m128i*)(buf + 16));
		}
		State[0] = _mm_xor_si128(State[0], Injection[0]);
		State[1] = _mm_xor_si128(State[1], Injection[1]);
		if (state_index == 32)
		{
			FPermAsm(State, StateOut);
			State[0] = StateOut[0];
			State[1] = StateOut[1];
			State[2] = StateOut[2];
			State[3] = StateOut[3];
			state_index = 0;
		}
	}//end of plaintext loading

	//Injecting AD
	while (adlen >ad_index) //we can load 32-byte chunks
	{
		if ((state_index == 0) && (adlen >= ad_index + 32))
		{
			Injection[0] = _mm_loadu_si128((__m128i*)(ad + ad_index));
			Injection[1] = _mm_loadu_si128((__m128i*)(ad + ad_index + 16));
			state_index = 32;
			ad_index += 32;
		}
		else
		{
			memset(buf, 0, 32);
			if ((32 - state_index) <= (adlen - ad_index))
			{
				memcpy(buf + state_index, ad + ad_index, 32 - state_index);
				ad_index += 32 - state_index;
				state_index = 32;
			}
			else
			{
				memcpy(buf + state_index, ad + ad_index, adlen - ad_index);
				state_index += adlen - ad_index;
				ad_index = adlen;
			}
			Injection[0] = _mm_loadu_si128((__m128i*)buf);
			Injection[1] = _mm_loadu_si128((__m128i*)(buf + 16));
		}
		State[0] = _mm_xor_si128(State[0], Injection[0]);
		State[1] = _mm_xor_si128(State[1], Injection[1]);
		if (state_index == 32)
		{
			FPermAsm(State, StateOut);

			State[0] = StateOut[0];
			State[1] = StateOut[1];
			State[2] = StateOut[2];
			State[3] = StateOut[3];
			state_index = 0;
		}
	}//end of AD loading

	//Injecting padding
	if (state_index < 31)
	{
		memset(buf, 0, 32);
		buf[state_index] = 1;
		buf[31] = 1;
		Injection[0] = _mm_loadu_si128((__m128i*)(buf ));
		Injection[1] = _mm_loadu_si128((__m128i*)(buf + 16)); 
		State[0] = _mm_xor_si128(State[0], Injection[0]);
		State[1] = _mm_xor_si128(State[1], Injection[1]);
		FPermAsm(State, StateOut);

		State[0] = StateOut[0];
		State[1] = StateOut[1];
		State[2] = StateOut[2];
		State[3] = StateOut[3];
	}
	else
	{
		Injection[1] = _mm_set_epi32(1 << 31, 0, 0, 0);
		State[1] = _mm_xor_si128(State[1], Injection[1]);
		FPermAsm(State, StateOut);

		State[0] = StateOut[0];
		State[1] = StateOut[1];
		State[2] = StateOut[2];
		State[3] = StateOut[3];
		State[1] = _mm_xor_si128(State[1], Injection[1]);
		FPermAsm(State, StateOut);

		State[0] = StateOut[0];
		State[1] = StateOut[1];
		State[2] = StateOut[2];
		State[3] = StateOut[3];
	}
	memcpy(output, StateOut, 32);
	return 0;

}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xf8, %rsp
movq %rcx, 0x58(%rsp)
movq %rdx, %rbx
movq %rsi, 0x50(%rsp)
movq %rdi, 0xa8(%rsp)
movq %r8, %rax
shlq $0x20, %rax
vmovq %rdx, %xmm0
vmovq %rax, %xmm1
vpunpcklqdq %xmm1, %xmm0, %xmm0 # xmm0 = xmm0[0],xmm1[0]
movq %r8, 0x18(%rsp)
movq %r8, %rax
shrq $0x20, %rax
vmovd %eax, %xmm1
vxorps %xmm2, %xmm2, %xmm2
leaq 0x60(%rsp), %r14
vmovups %ymm2, 0x20(%r14)
vmovupd (%r9), %xmm2
vmovapd %xmm2, (%rsp)
vpslldq $0xa, %xmm2, %xmm2      # xmm2 = zero,zero,zero,zero,zero,zero,zero,zero,zero,zero,xmm2[0,1,2,3,4,5]
vpternlogq $0x96, 0x146c(%rip), %xmm1, %xmm2 # 0x40a0
vmovdqa %xmm0, (%r14)
vmovdqa %xmm2, 0x10(%r14)
leaq 0xb0(%rsp), %r15
movq %r14, %rdi
movq %r15, %rsi
vzeroupper
callq 0x10b0
vpsrldq $0x6, (%rsp), %xmm0     # xmm0 = mem[6,7,8,9,10,11,12,13,14,15],zero,zero,zero,zero,zero,zero
vxorpd (%r15), %xmm0, %xmm2
vmovapd %xmm2, (%r14)
vmovaps 0x10(%r15), %xmm1
vmovups 0x20(%r15), %ymm0
vmovups %ymm0, 0x20(%r14)
pushq $0xa
popq %r15
xorl %r14d, %r14d
pushq $0x20
popq %r13
movq %rbx, %r12
subq %r14, %r12
jbe 0x2d8b
testl %r15d, %r15d
jne 0x2ca2
leaq 0x20(%r14), %rcx
cmpq %rbx, %rcx
jbe 0x2d7b
vmovapd %xmm2, 0x20(%rsp)
vmovaps %xmm1, (%rsp)
vxorps %xmm0, %xmm0, %xmm0
vmovups %ymm0, 0x30(%rsp)
movl %r13d, %ebp
subl %r15d, %ebp
movq 0x50(%rsp), %rax
leaq (%rax,%r14), %rsi
movl %r15d, %eax
leaq (%rsp,%rax), %rdi
addq $0x30, %rdi
cmpq %rbp, %r12
jae 0x2cee
movq %r12, %rdx
vzeroupper
callq 0x10d0
addl %r12d, %r15d
leaq 0x30(%rsp), %rax
movq %rbx, %r14
jmp 0x2d04
movq %rbp, %rdx
vzeroupper
callq 0x10d0
addq %rbp, %r14
leaq 0x30(%rsp), %rax
movl %r13d, %r15d
vmovaps (%rsp), %xmm1
vmovapd 0x20(%rsp), %xmm2
vxorpd (%rax), %xmm2, %xmm2
vxorps 0x10(%rax), %xmm1, %xmm1
vmovapd %xmm2, 0x60(%rsp)
vmovaps %xmm1, 0x70(%rsp)
cmpl $0x20, %r15d
jne 0x2c84
leaq 0x60(%rsp), %rdi
leaq 0xb0(%rsp), %rsi
vzeroupper
callq 0x10b0
vmovapd 0xb0(%rsp), %xmm2
vmovaps 0xc0(%rsp), %xmm1
vmovapd %xmm2, 0x60(%rsp)
vmovaps %xmm1, 0x70(%rsp)
vmovups 0xd0(%rsp), %ymm0
vmovups %ymm0, 0x80(%rsp)
xorl %r15d, %r15d
jmp 0x2c84
addq 0x50(%rsp), %r14
movq %r14, %rax
movl %r13d, %r15d
movq %rcx, %r14
jmp 0x2d0f
xorl %r14d, %r14d
pushq $0x20
popq %rbx
leaq 0x30(%rsp), %r12
movq 0x18(%rsp), %r13
movl %r15d, %eax
subq %r14, %r13
jbe 0x2e9e
testl %r15d, %r15d
jne 0x2dbb
leaq 0x20(%r14), %rcx
cmpq 0x18(%rsp), %rcx
jbe 0x2e8e
vmovapd %xmm2, 0x20(%rsp)
vmovaps %xmm1, (%rsp)
vxorps %xmm0, %xmm0, %xmm0
vmovups %ymm0, 0x30(%rsp)
movl %ebx, %ebp
subl %r15d, %ebp
movq 0x58(%rsp), %rcx
leaq (%rcx,%r14), %rsi
leaq (%rsp,%rax), %rdi
addq $0x30, %rdi
cmpq %rbp, %r13
jae 0x2e03
movq %r13, %rdx
vzeroupper
callq 0x10d0
addl %r13d, %r15d
movq %r12, %rax
movq 0x18(%rsp), %r14
jmp 0x2e17
movq %rbp, %rdx
vzeroupper
callq 0x10d0
addq %rbp, %r14
movq %r12, %rax
movl %ebx, %r15d
vmovaps (%rsp), %xmm1
vmovapd 0x20(%rsp), %xmm2
vxorpd (%rax), %xmm2, %xmm2
vxorps 0x10(%rax), %xmm1, %xmm1
vmovapd %xmm2, 0x60(%rsp)
vmovaps %xmm1, 0x70(%rsp)
cmpl $0x20, %r15d
jne 0x2d96
leaq 0x60(%rsp), %rdi
leaq 0xb0(%rsp), %rsi
vzeroupper
callq 0x10b0
vmovapd 0xb0(%rsp), %xmm2
vmovaps 0xc0(%rsp), %xmm1
vmovapd %xmm2, 0x60(%rsp)
vmovaps %xmm1, 0x70(%rsp)
vmovups 0xd0(%rsp), %ymm0
vmovups %ymm0, 0x80(%rsp)
xorl %r15d, %r15d
jmp 0x2d96
addq 0x58(%rsp), %r14
movq %r14, %rax
movl %ebx, %r15d
movq %rcx, %r14
jmp 0x2e22
cmpl $0x1e, %r15d
ja 0x2ecc
vxorps %xmm0, %xmm0, %xmm0
vmovups %ymm0, 0x30(%rsp)
movb $0x1, %cl
movb %cl, 0x30(%rsp,%rax)
movb %cl, 0x4f(%rsp)
vxorpd 0x30(%rsp), %xmm2, %xmm0
vmovapd %xmm0, 0x60(%rsp)
vxorps 0x40(%rsp), %xmm1, %xmm0
jmp 0x2f16
vxorps 0x11dc(%rip), %xmm1, %xmm0 # 0x40b0
leaq 0x60(%rsp), %rbx
vmovaps %xmm0, 0x10(%rbx)
leaq 0xb0(%rsp), %r14
movq %rbx, %rdi
movq %r14, %rsi
vzeroupper
callq 0x10b0
vmovaps (%r14), %xmm0
vmovaps %xmm0, (%rbx)
vmovups 0x20(%r14), %ymm0
vmovups %ymm0, 0x20(%rbx)
vmovaps 0x11a0(%rip), %xmm0     # 0x40b0
vxorps 0x10(%r14), %xmm0, %xmm0
leaq 0x60(%rsp), %rdi
vmovaps %xmm0, 0x10(%rdi)
leaq 0xb0(%rsp), %rbx
movq %rbx, %rsi
vzeroupper
callq 0x10b0
vmovups (%rbx), %ymm0
movq 0xa8(%rsp), %rax
vmovups %ymm0, (%rax)
xorl %eax, %eax
addq $0xf8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
vzeroupper
retq