Message ID | 1353611587-18186-11-git-send-email-sjg@chromium.org |
---|---|
State | Superseded, archived |
Headers | show |
Hi Simon, On Thu, Nov 22, 2012 at 1:12 PM, Simon Glass <sjg@chromium.org> wrote: > 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 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 > > diff --git a/common/Makefile b/common/Makefile > index 84968f8..eb175c1 100644 > --- a/common/Makefile > +++ b/common/Makefile > @@ -31,6 +31,7 @@ COBJS-y += main.o > COBJS-y += command.o > COBJS-y += exports.o > COBJS-$(CONFIG_SYS_HUSH_PARSER) += hush.o > +COBJS-y += hash.o Why not put them in alphabetical order? > COBJS-y += s_record.o > COBJS-y += xyzModem.o > COBJS-y += cmd_disk.o > diff --git a/common/hash.c b/common/hash.c > new file mode 100644 > index 0000000..9a844b8 > --- /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 (!stricmp(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 SHA1 sum\n", > + *argv); You should probably use the name of the algo in the error message, not a fixed SHA1. > + 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 Otherwise, looks good. -Joe
diff --git a/common/Makefile b/common/Makefile index 84968f8..eb175c1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -31,6 +31,7 @@ COBJS-y += main.o COBJS-y += command.o COBJS-y += exports.o COBJS-$(CONFIG_SYS_HUSH_PARSER) += hush.o +COBJS-y += hash.o COBJS-y += s_record.o COBJS-y += xyzModem.o COBJS-y += cmd_disk.o diff --git a/common/hash.c b/common/hash.c new file mode 100644 index 0000000..9a844b8 --- /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 (!stricmp(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 SHA1 sum\n", + *argv); + 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
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 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