Patchwork [U-Boot,v3,09/20] Add generic hash API

login
register
mail settings
Submitter Simon Glass
Date Dec. 6, 2012, 12:46 a.m.
Message ID <1354754807-21449-10-git-send-email-sjg@chromium.org>
Download mbox | patch
Permalink /patch/204078/
State Accepted, archived
Delegated to: Tom Rini
Headers show

Comments

Simon Glass - Dec. 6, 2012, 12:46 a.m.
We have a SHA1 command and want to add a SHA256 command also. Instead of
duplicating the code, create a generic hash API which can process
commands for different algorithms.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Rename stricmp() to strcasecmp() to match Linux / POSIX
- Correct hash error message to show the algorithm name, not always SHA1
- Fix ordering of hash.o in Makefile

Changes in v2:
- Add generic hash API to allow SHA256 command to be added without duplication

 common/Makefile |    1 +
 common/hash.c   |  221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hash.h  |   69 +++++++++++++++++
 3 files changed, 291 insertions(+), 0 deletions(-)
 create mode 100644 common/hash.c
 create mode 100644 include/hash.h

Patch

diff --git a/common/Makefile b/common/Makefile
index 84968f8..c451c3d 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -30,6 +30,7 @@  ifndef CONFIG_SPL_BUILD
 COBJS-y += main.o
 COBJS-y += command.o
 COBJS-y += exports.o
+COBJS-y += hash.o
 COBJS-$(CONFIG_SYS_HUSH_PARSER) += hush.o
 COBJS-y += s_record.o
 COBJS-y += xyzModem.o
diff --git a/common/hash.c b/common/hash.c
new file mode 100644
index 0000000..e3a6e43
--- /dev/null
+++ b/common/hash.c
@@ -0,0 +1,221 @@ 
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * (C) Copyright 2011
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+#include <hash.h>
+#include <sha1.h>
+#include <sha256.h>
+
+/*
+ * These are the hash algorithms we support. Chips which support accelerated
+ * crypto could perhaps add named version of these algorithms here.
+ */
+static struct hash_algo hash_algo[] = {
+#ifdef CONFIG_SHA1
+	{
+		"SHA1",
+		SHA1_SUM_LEN,
+		sha1_csum_wd,
+		CHUNKSZ_SHA1,
+	},
+#endif
+#ifdef CONFIG_SHA256
+	{
+		"SHA256",
+		SHA256_SUM_LEN,
+		sha256_csum_wd,
+		CHUNKSZ_SHA256,
+	},
+#endif
+};
+
+/**
+ * store_result: Store the resulting sum to an address or variable
+ *
+ * @algo:		Hash algorithm being used
+ * @sum:		Hash digest (algo->digest_size bytes)
+ * @dest:		Destination, interpreted as a hex address if it starts
+ *			with * or otherwise as an environment variable.
+ */
+static void store_result(struct hash_algo *algo, const u8 *sum,
+			 const char *dest)
+{
+	unsigned int i;
+
+	if (*dest == '*') {
+		u8 *ptr;
+
+		ptr = (u8 *)simple_strtoul(dest + 1, NULL, 16);
+		memcpy(ptr, sum, algo->digest_size);
+	} else {
+		char str_output[HASH_MAX_DIGEST_SIZE * 2 + 1];
+		char *str_ptr = str_output;
+
+		for (i = 0; i < algo->digest_size; i++) {
+			sprintf(str_ptr, "%02x", sum[i]);
+			str_ptr += 2;
+		}
+		str_ptr = '\0';
+		setenv(dest, str_output);
+	}
+}
+
+/**
+ * parse_verify_sum: Parse a hash verification parameter
+ *
+ * @algo:		Hash algorithm being used
+ * @verify_str:		Argument to parse. If it starts with * then it is
+ *			interpreted as a hex address containing the hash.
+ *			If the length is exactly the right number of hex digits
+ *			for the digest size, then we assume it is a hex digest.
+ *			Otherwise we assume it is an environment variable, and
+ *			look up its value (it must contain a hex digest).
+ * @vsum:		Returns binary digest value (algo->digest_size bytes)
+ * @return 0 if ok, non-zero on error
+ */
+static int parse_verify_sum(struct hash_algo *algo, char *verify_str, u8 *vsum)
+{
+	if (*verify_str == '*') {
+		u8 *ptr;
+
+		ptr = (u8 *)simple_strtoul(verify_str + 1, NULL, 16);
+		memcpy(vsum, ptr, algo->digest_size);
+	} else {
+		unsigned int i;
+		char *vsum_str;
+		int digits = algo->digest_size * 2;
+
+		/*
+		 * As with the original code from sha1sum.c, we assume that a
+		 * string which matches the digest size exactly is a hex
+		 * string and not an environment variable.
+		 */
+		if (strlen(verify_str) == digits)
+			vsum_str = verify_str;
+		else {
+			vsum_str = getenv(verify_str);
+			if (vsum_str == NULL || strlen(vsum_str) != digits) {
+				printf("Expected %d hex digits in env var\n",
+				       digits);
+				return 1;
+			}
+		}
+
+		for (i = 0; i < algo->digest_size; i++) {
+			char *nullp = vsum_str + (i + 1) * 2;
+			char end = *nullp;
+
+			*nullp = '\0';
+			vsum[i] = simple_strtoul(vsum_str + (i * 2), NULL, 16);
+			*nullp = end;
+		}
+	}
+	return 0;
+}
+
+static struct hash_algo *find_hash_algo(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_algo); i++) {
+		if (!strcasecmp(name, hash_algo[i].name))
+			return &hash_algo[i];
+	}
+
+	return NULL;
+}
+
+static void show_hash(struct hash_algo *algo, ulong addr, ulong len,
+		      u8 *output)
+{
+	int i;
+
+	printf("%s for %08lx ... %08lx ==> ", algo->name, addr, addr + len - 1);
+	for (i = 0; i < algo->digest_size; i++)
+		printf("%02x", output[i]);
+}
+
+int hash_command(const char *algo_name, int verify, cmd_tbl_t *cmdtp, int flag,
+		 int argc, char * const argv[])
+{
+	struct hash_algo *algo;
+	ulong addr, len;
+	u8 output[HASH_MAX_DIGEST_SIZE];
+	u8 vsum[HASH_MAX_DIGEST_SIZE];
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	algo = find_hash_algo(algo_name);
+	if (!algo) {
+		printf("Unknown hash algorithm '%s'\n", algo_name);
+		return CMD_RET_USAGE;
+	}
+	addr = simple_strtoul(*argv++, NULL, 16);
+	len = simple_strtoul(*argv++, NULL, 16);
+	argc -= 2;
+
+	if (algo->digest_size > HASH_MAX_DIGEST_SIZE) {
+		puts("HASH_MAX_DIGEST_SIZE exceeded\n");
+		return 1;
+	}
+
+	algo->hash_func_ws((const unsigned char *)addr, len, output,
+			   algo->chunk_size);
+
+	/* Try to avoid code bloat when verify is not needed */
+#ifdef CONFIG_HASH_VERIFY
+	if (verify) {
+#else
+	if (0) {
+#endif
+		if (!argc)
+			return CMD_RET_USAGE;
+		if (parse_verify_sum(algo, *argv, vsum)) {
+			printf("ERROR: %s does not contain a valid %s sum\n",
+				*argv, algo->name);
+			return 1;
+		}
+		if (memcmp(output, vsum, algo->digest_size) != 0) {
+			int i;
+
+			show_hash(algo, addr, len, output);
+			printf(" != ");
+			for (i = 0; i < algo->digest_size; i++)
+				printf("%02x", vsum[i]);
+			puts(" ** ERROR **\n");
+			return 1;
+		}
+	} else {
+		show_hash(algo, addr, len, output);
+		printf("\n");
+
+		if (argc)
+			store_result(algo, output, *argv);
+	}
+
+	return 0;
+}
diff --git a/include/hash.h b/include/hash.h
new file mode 100644
index 0000000..34ba558
--- /dev/null
+++ b/include/hash.h
@@ -0,0 +1,69 @@ 
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#ifdef CONFIG_SHA1SUM_VERIFY
+#define CONFIG_HASH_VERIFY
+#endif
+
+struct hash_algo {
+	const char *name;			/* Name of algorithm */
+	int digest_size;			/* Length of digest */
+	/**
+	 * hash_func_ws: Generic hashing function
+	 *
+	 * This is the generic prototype for a hashing function. We only
+	 * have the watchdog version at present.
+	 *
+	 * @input:	Input buffer
+	 * @ilen:	Input buffer length
+	 * @output:	Checksum result (length depends on algorithm)
+	 * @chunk_sz:	Trigger watchdog after processing this many bytes
+	 */
+	void (*hash_func_ws)(const unsigned char *input, unsigned int ilen,
+		unsigned char *output, unsigned int chunk_sz);
+	int chunk_size;				/* Watchdog chunk size */
+};
+
+/*
+ * Maximum digest size for all algorithms we support. Having this value
+ * avoids a malloc() or C99 local declaration in common/cmd_hash.c.
+ */
+#define HASH_MAX_DIGEST_SIZE	32
+
+/**
+ * hash_command: Process a hash command for a particular algorithm
+ *
+ * This common function is used to implement specific hash commands.
+ *
+ * @algo_name:		Hash algorithm being used
+ * @verify:		Non-zero to enable verify mode
+ * @cmdtp:		Pointer to command table entry
+ * @flag:		Some flags normally 0 (see CMD_FLAG_.. above)
+ * @argc:		Number of arguments (arg 0 must be the command text)
+ * @argv:		Arguments
+ */
+int hash_command(const char *algo_name, int verify, cmd_tbl_t *cmdtp, int flag,
+		 int argc, char * const argv[]);
+
+#endif