SHAKE-128 example


/* This program is released under the Common Public License V1.0
 *
 * You should have received a copy of Common Public License V1.0 along with
 * with this program.
 *
 * Copyright IBM Corp. 2017 
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ica_api.h>

/* The name of the file to calculate the SHAKE-128 hash from */
#define FILE_NAME "example_shake_128.c"

/* Size of the chunks in which the file is read.
 * Must be a multiple of 168 bytes (the SHAKE-128 block size).
 */
#define CHUNK_SIZE 168

/* An arbitrary output_length in case the user did not specify a value via args */
#define SAMPLE_SHAKE_OUTPUT_LENGTH 123

/* Prints hex values to standard out. */
static void dump_data(unsigned char *data, unsigned long length);

/* Prints a description of the return value to standard out. */
static int handle_ica_error(int rc);

int main(int argc, char **argv)
{
	int rc=0;
	unsigned int output_length = SAMPLE_SHAKE_OUTPUT_LENGTH;

	/* Try to read the user specified output length. If none given, use our
	 * sample value.
	 */
	if (argc > 1 && argv[1] != NULL)
		output_length = atoi(argv[1]);

	/* This is the buffer where the SHAKE-128 hash is generated into.
	 * The SHAKE algorithm can create output of any length greater or equal
	 * to 8 bytes. Let's use an output length of 256 bytes for this example.
	 */
	unsigned char* shake_result_p;

	/* The file will be read in several chunks into this buffer.
	 * The chunks will be the input to the ica_shake_128 function which
	 * we call for each chunk.
	 */
	unsigned char shake_input[CHUNK_SIZE];

	/* This is the SHAKE-128 context. It stores intermediate values
	 * needed when chaining multiple chunks (as we do).
	 */
	shake_128_context_t context;

	/* Open the file in binary mode and read its content in chunks */
	FILE *f;
	f = fopen(FILE_NAME, "r");
	if (f == NULL)
		return handle_ica_error(errno);

	/* Allocate a buffer for the output value */
	shake_result_p = malloc(output_length);
	if (shake_result_p == NULL) {
		printf("Cannot malloc %d bytes for output value. \n", output_length);
		return EINVAL;
	}

	/* Perform the shake-128 operation ... */
	int len;
	unsigned long total_size = 0;
	memset((char*)&context, 0, sizeof(context));
	while (!feof(f)) {
		/* read a chunk of data */
		len = fread(shake_input, 1, CHUNK_SIZE, f);
		if (total_size == 0) {
			/* this is the first chunk */
			rc = ica_shake_128(SHA_MSG_PART_FIRST, len, shake_input,
					&context, shake_result_p, output_length);
		} else if (!feof(f)) {
			/* add this chunk to the hash */
			rc = ica_shake_128(SHA_MSG_PART_MIDDLE, len, shake_input,
					&context, shake_result_p, output_length);
		} else {
			/* this is the last chunk */
			rc = ica_shake_128(SHA_MSG_PART_FINAL, len, shake_input,
					&context, shake_result_p, output_length);
		}

		total_size += len;
		if (rc)
			break;
	}

	/* close the file */
	fclose(f);

	/* Error handling (if necessary). */
	if (rc)
		return handle_ica_error(rc);

	/* Dump the generated hash to standard output, just for
	 * a visual control.
	 */
	printf("SHAKE-128 hash with %d bytes of file '%s' (%lu bytes):\n", output_length,
			FILE_NAME, total_size);

	dump_data(shake_result_p, output_length);
}

static void dump_data(unsigned char *data, unsigned long length)
{
	unsigned char *ptr;
	int i;
	for (ptr = data, i = 1; ptr < (data + length); ptr++, i++) {
		printf("0x%02x ", *ptr);
		if ((i % 16) == 0)
			printf("\n");
	}
	if (i % 16)
		printf("\n");
}

static int handle_ica_error(int rc)
{
	switch (rc) {
	case 0:
		printf("OK\n");
		break;
	case EINVAL:
		printf("Incorrect parameter.\n");
		break;
	case EPERM:
		printf("Operation not permitted by Hardware (CPACF).\n");
		break;
	case EIO:
		printf("I/O error.\n");
		break;
	default:
		printf("unknown error.\n");
	}
	return rc;
}