/*
 * sip_keyscan.c -- demonstrate DF-3564: SipHash_SetKey() retains a verbatim
 * copy of the 128-bit key in stack local k[2] (sys/crypto/siphash/siphash.c:71)
 * after it returns, with no zeroization (contrast SipHash_End which does
 * bzero(ctx)).  Same-source userland build of the kernel implementation.
 *
 * The scan uses an inline byte loop (no calls) so the comparison itself
 * cannot clobber the dead frame being inspected.
 */
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>

#include <crypto/siphash/siphash.h>

static unsigned char the_key[16];
static volatile uint64_t sink;

__attribute__((noinline)) static void
do_sip_setkey(void)
{
	SIPHASH_CTX ctx;

	SipHash24_Init(&ctx);
	SipHash_SetKey(&ctx, the_key);
	/* return immediately: nothing else may run at this stack depth */
	sink = ctx.v[0];
}

static int
key_at(const unsigned char *p)
{
	int j;

	for (j = 0; j < 16; j++)
		if (p[j] != the_key[j])
			return 0;
	return 1;
}

int
main(void)
{
	unsigned char probe[64];
	unsigned char *p, *start, *low;
	int found = 0;
	int i;

	for (i = 0; i < 16; i++)
		the_key[i] = (unsigned char)(0xA0 + i);

	memset(probe, 0, sizeof probe);
	do_sip_setkey();	/* the key copy must be dead after this */

	start = (unsigned char *)&probe;
	low = start - 32768;	/* callee frames live below us */
	for (p = start - 16; p >= low; p--) {
		if (key_at(p)) {	/* inline compare: no call frame */
			found = 1;
			printf("RETAINED: verbatim 16-byte key found on the "
			    "dead stack, offset -%td below caller frame\n",
			    (ptrdiff_t)(start - p));
			break;
		}
	}
	if (!found)
		printf("NOT FOUND: key copy was not retained on stack\n");
	printf("%s: raw key bytes %s the stack after SipHash_SetKey "
	    "returned\n", found ? "DF-3564 CONFIRMED" : "not confirmed",
	    found ? "persist on" : "do not persist on");
	return found ? 0 : 1;
}
