uboot/common/hash.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012 The Chromium OS Authors.
   3 *
   4 * (C) Copyright 2011
   5 * Joe Hershberger, National Instruments, joe.hershberger@ni.com
   6 *
   7 * (C) Copyright 2000
   8 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License as
  12 * published by the Free Software Foundation; either version 2 of
  13 * the License, or (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  23 * MA 02111-1307 USA
  24 */
  25
  26#include <common.h>
  27#include <command.h>
  28#include <hash.h>
  29#include <sha1.h>
  30#include <sha256.h>
  31
  32/*
  33 * These are the hash algorithms we support. Chips which support accelerated
  34 * crypto could perhaps add named version of these algorithms here.
  35 */
  36static struct hash_algo hash_algo[] = {
  37#ifdef CONFIG_SHA1
  38        {
  39                "SHA1",
  40                SHA1_SUM_LEN,
  41                sha1_csum_wd,
  42                CHUNKSZ_SHA1,
  43        },
  44#endif
  45#ifdef CONFIG_SHA256
  46        {
  47                "SHA256",
  48                SHA256_SUM_LEN,
  49                sha256_csum_wd,
  50                CHUNKSZ_SHA256,
  51        },
  52#endif
  53};
  54
  55/**
  56 * store_result: Store the resulting sum to an address or variable
  57 *
  58 * @algo:               Hash algorithm being used
  59 * @sum:                Hash digest (algo->digest_size bytes)
  60 * @dest:               Destination, interpreted as a hex address if it starts
  61 *                      with * or otherwise as an environment variable.
  62 */
  63static void store_result(struct hash_algo *algo, const u8 *sum,
  64                         const char *dest)
  65{
  66        unsigned int i;
  67
  68        if (*dest == '*') {
  69                u8 *ptr;
  70
  71                ptr = (u8 *)simple_strtoul(dest + 1, NULL, 16);
  72                memcpy(ptr, sum, algo->digest_size);
  73        } else {
  74                char str_output[HASH_MAX_DIGEST_SIZE * 2 + 1];
  75                char *str_ptr = str_output;
  76
  77                for (i = 0; i < algo->digest_size; i++) {
  78                        sprintf(str_ptr, "%02x", sum[i]);
  79                        str_ptr += 2;
  80                }
  81                str_ptr = '\0';
  82                setenv(dest, str_output);
  83        }
  84}
  85
  86/**
  87 * parse_verify_sum: Parse a hash verification parameter
  88 *
  89 * @algo:               Hash algorithm being used
  90 * @verify_str:         Argument to parse. If it starts with * then it is
  91 *                      interpreted as a hex address containing the hash.
  92 *                      If the length is exactly the right number of hex digits
  93 *                      for the digest size, then we assume it is a hex digest.
  94 *                      Otherwise we assume it is an environment variable, and
  95 *                      look up its value (it must contain a hex digest).
  96 * @vsum:               Returns binary digest value (algo->digest_size bytes)
  97 * @return 0 if ok, non-zero on error
  98 */
  99static int parse_verify_sum(struct hash_algo *algo, char *verify_str, u8 *vsum)
 100{
 101        if (*verify_str == '*') {
 102                u8 *ptr;
 103
 104                ptr = (u8 *)simple_strtoul(verify_str + 1, NULL, 16);
 105                memcpy(vsum, ptr, algo->digest_size);
 106        } else {
 107                unsigned int i;
 108                char *vsum_str;
 109                int digits = algo->digest_size * 2;
 110
 111                /*
 112                 * As with the original code from sha1sum.c, we assume that a
 113                 * string which matches the digest size exactly is a hex
 114                 * string and not an environment variable.
 115                 */
 116                if (strlen(verify_str) == digits)
 117                        vsum_str = verify_str;
 118                else {
 119                        vsum_str = getenv(verify_str);
 120                        if (vsum_str == NULL || strlen(vsum_str) != digits) {
 121                                printf("Expected %d hex digits in env var\n",
 122                                       digits);
 123                                return 1;
 124                        }
 125                }
 126
 127                for (i = 0; i < algo->digest_size; i++) {
 128                        char *nullp = vsum_str + (i + 1) * 2;
 129                        char end = *nullp;
 130
 131                        *nullp = '\0';
 132                        vsum[i] = simple_strtoul(vsum_str + (i * 2), NULL, 16);
 133                        *nullp = end;
 134                }
 135        }
 136        return 0;
 137}
 138
 139static struct hash_algo *find_hash_algo(const char *name)
 140{
 141        int i;
 142
 143        for (i = 0; i < ARRAY_SIZE(hash_algo); i++) {
 144                if (!strcasecmp(name, hash_algo[i].name))
 145                        return &hash_algo[i];
 146        }
 147
 148        return NULL;
 149}
 150
 151static void show_hash(struct hash_algo *algo, ulong addr, ulong len,
 152                      u8 *output)
 153{
 154        int i;
 155
 156        printf("%s for %08lx ... %08lx ==> ", algo->name, addr, addr + len - 1);
 157        for (i = 0; i < algo->digest_size; i++)
 158                printf("%02x", output[i]);
 159}
 160
 161int hash_command(const char *algo_name, int verify, cmd_tbl_t *cmdtp, int flag,
 162                 int argc, char * const argv[])
 163{
 164        struct hash_algo *algo;
 165        ulong addr, len;
 166        u8 output[HASH_MAX_DIGEST_SIZE];
 167        u8 vsum[HASH_MAX_DIGEST_SIZE];
 168
 169        if (argc < 2)
 170                return CMD_RET_USAGE;
 171
 172        algo = find_hash_algo(algo_name);
 173        if (!algo) {
 174                printf("Unknown hash algorithm '%s'\n", algo_name);
 175                return CMD_RET_USAGE;
 176        }
 177        addr = simple_strtoul(*argv++, NULL, 16);
 178        len = simple_strtoul(*argv++, NULL, 16);
 179        argc -= 2;
 180
 181        if (algo->digest_size > HASH_MAX_DIGEST_SIZE) {
 182                puts("HASH_MAX_DIGEST_SIZE exceeded\n");
 183                return 1;
 184        }
 185
 186        algo->hash_func_ws((const unsigned char *)addr, len, output,
 187                           algo->chunk_size);
 188
 189        /* Try to avoid code bloat when verify is not needed */
 190#ifdef CONFIG_HASH_VERIFY
 191        if (verify) {
 192#else
 193        if (0) {
 194#endif
 195                if (!argc)
 196                        return CMD_RET_USAGE;
 197                if (parse_verify_sum(algo, *argv, vsum)) {
 198                        printf("ERROR: %s does not contain a valid %s sum\n",
 199                                *argv, algo->name);
 200                        return 1;
 201                }
 202                if (memcmp(output, vsum, algo->digest_size) != 0) {
 203                        int i;
 204
 205                        show_hash(algo, addr, len, output);
 206                        printf(" != ");
 207                        for (i = 0; i < algo->digest_size; i++)
 208                                printf("%02x", vsum[i]);
 209                        puts(" ** ERROR **\n");
 210                        return 1;
 211                }
 212        } else {
 213                show_hash(algo, addr, len, output);
 214                printf("\n");
 215
 216                if (argc)
 217                        store_result(algo, output, *argv);
 218        }
 219
 220        return 0;
 221}
 222