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 * SPDX-License-Identifier:     GPL-2.0+
  11 */
  12
  13#ifndef USE_HOSTCC
  14#include <common.h>
  15#include <command.h>
  16#include <malloc.h>
  17#include <mapmem.h>
  18#include <hw_sha.h>
  19#include <asm/io.h>
  20#include <linux/errno.h>
  21#else
  22#include "mkimage.h"
  23#include <time.h>
  24#include <image.h>
  25#endif /* !USE_HOSTCC*/
  26
  27#include <hash.h>
  28#include <u-boot/crc.h>
  29#include <u-boot/sha1.h>
  30#include <u-boot/sha256.h>
  31#include <u-boot/md5.h>
  32
  33#if defined(CONFIG_SHA1) && !defined(CONFIG_SHA_PROG_HW_ACCEL)
  34static int hash_init_sha1(struct hash_algo *algo, void **ctxp)
  35{
  36        sha1_context *ctx = malloc(sizeof(sha1_context));
  37        sha1_starts(ctx);
  38        *ctxp = ctx;
  39        return 0;
  40}
  41
  42static int hash_update_sha1(struct hash_algo *algo, void *ctx, const void *buf,
  43                            unsigned int size, int is_last)
  44{
  45        sha1_update((sha1_context *)ctx, buf, size);
  46        return 0;
  47}
  48
  49static int hash_finish_sha1(struct hash_algo *algo, void *ctx, void *dest_buf,
  50                            int size)
  51{
  52        if (size < algo->digest_size)
  53                return -1;
  54
  55        sha1_finish((sha1_context *)ctx, dest_buf);
  56        free(ctx);
  57        return 0;
  58}
  59#endif
  60
  61#if defined(CONFIG_SHA256) && !defined(CONFIG_SHA_PROG_HW_ACCEL)
  62static int hash_init_sha256(struct hash_algo *algo, void **ctxp)
  63{
  64        sha256_context *ctx = malloc(sizeof(sha256_context));
  65        sha256_starts(ctx);
  66        *ctxp = ctx;
  67        return 0;
  68}
  69
  70static int hash_update_sha256(struct hash_algo *algo, void *ctx,
  71                              const void *buf, unsigned int size, int is_last)
  72{
  73        sha256_update((sha256_context *)ctx, buf, size);
  74        return 0;
  75}
  76
  77static int hash_finish_sha256(struct hash_algo *algo, void *ctx, void
  78                              *dest_buf, int size)
  79{
  80        if (size < algo->digest_size)
  81                return -1;
  82
  83        sha256_finish((sha256_context *)ctx, dest_buf);
  84        free(ctx);
  85        return 0;
  86}
  87#endif
  88
  89static int hash_init_crc32(struct hash_algo *algo, void **ctxp)
  90{
  91        uint32_t *ctx = malloc(sizeof(uint32_t));
  92        *ctx = 0;
  93        *ctxp = ctx;
  94        return 0;
  95}
  96
  97static int hash_update_crc32(struct hash_algo *algo, void *ctx,
  98                             const void *buf, unsigned int size, int is_last)
  99{
 100        *((uint32_t *)ctx) = crc32(*((uint32_t *)ctx), buf, size);
 101        return 0;
 102}
 103
 104static int hash_finish_crc32(struct hash_algo *algo, void *ctx, void *dest_buf,
 105                             int size)
 106{
 107        if (size < algo->digest_size)
 108                return -1;
 109
 110        *((uint32_t *)dest_buf) = *((uint32_t *)ctx);
 111        free(ctx);
 112        return 0;
 113}
 114
 115/*
 116 * These are the hash algorithms we support.  If we have hardware acceleration
 117 * is enable we will use that, otherwise a software version of the algorithm.
 118 * Note that algorithm names must be in lower case.
 119 */
 120static struct hash_algo hash_algo[] = {
 121#ifdef CONFIG_SHA1
 122        {
 123                .name           = "sha1",
 124                .digest_size    = SHA1_SUM_LEN,
 125                .chunk_size     = CHUNKSZ_SHA1,
 126#ifdef CONFIG_SHA_HW_ACCEL
 127                .hash_func_ws   = hw_sha1,
 128#else
 129                .hash_func_ws   = sha1_csum_wd,
 130#endif
 131#ifdef CONFIG_SHA_PROG_HW_ACCEL
 132                .hash_init      = hw_sha_init,
 133                .hash_update    = hw_sha_update,
 134                .hash_finish    = hw_sha_finish,
 135#else
 136                .hash_init      = hash_init_sha1,
 137                .hash_update    = hash_update_sha1,
 138                .hash_finish    = hash_finish_sha1,
 139#endif
 140        },
 141#endif
 142#ifdef CONFIG_SHA256
 143        {
 144                .name           = "sha256",
 145                .digest_size    = SHA256_SUM_LEN,
 146                .chunk_size     = CHUNKSZ_SHA256,
 147#ifdef CONFIG_SHA_HW_ACCEL
 148                .hash_func_ws   = hw_sha256,
 149#else
 150                .hash_func_ws   = sha256_csum_wd,
 151#endif
 152#ifdef CONFIG_SHA_PROG_HW_ACCEL
 153                .hash_init      = hw_sha_init,
 154                .hash_update    = hw_sha_update,
 155                .hash_finish    = hw_sha_finish,
 156#else
 157                .hash_init      = hash_init_sha256,
 158                .hash_update    = hash_update_sha256,
 159                .hash_finish    = hash_finish_sha256,
 160#endif
 161        },
 162#endif
 163        {
 164                .name           = "crc32",
 165                .digest_size    = 4,
 166                .chunk_size     = CHUNKSZ_CRC32,
 167                .hash_func_ws   = crc32_wd_buf,
 168                .hash_init      = hash_init_crc32,
 169                .hash_update    = hash_update_crc32,
 170                .hash_finish    = hash_finish_crc32,
 171        },
 172};
 173
 174/* Try to minimize code size for boards that don't want much hashing */
 175#if defined(CONFIG_SHA256) || defined(CONFIG_CMD_SHA1SUM) || \
 176        defined(CONFIG_CRC32_VERIFY) || defined(CONFIG_CMD_HASH)
 177#define multi_hash()    1
 178#else
 179#define multi_hash()    0
 180#endif
 181
 182int hash_lookup_algo(const char *algo_name, struct hash_algo **algop)
 183{
 184        int i;
 185
 186        for (i = 0; i < ARRAY_SIZE(hash_algo); i++) {
 187                if (!strcmp(algo_name, hash_algo[i].name)) {
 188                        *algop = &hash_algo[i];
 189                        return 0;
 190                }
 191        }
 192
 193        debug("Unknown hash algorithm '%s'\n", algo_name);
 194        return -EPROTONOSUPPORT;
 195}
 196
 197int hash_progressive_lookup_algo(const char *algo_name,
 198                                 struct hash_algo **algop)
 199{
 200        int i;
 201
 202        for (i = 0; i < ARRAY_SIZE(hash_algo); i++) {
 203                if (!strcmp(algo_name, hash_algo[i].name)) {
 204                        if (hash_algo[i].hash_init) {
 205                                *algop = &hash_algo[i];
 206                                return 0;
 207                        }
 208                }
 209        }
 210
 211        debug("Unknown hash algorithm '%s'\n", algo_name);
 212        return -EPROTONOSUPPORT;
 213}
 214
 215#ifndef USE_HOSTCC
 216int hash_parse_string(const char *algo_name, const char *str, uint8_t *result)
 217{
 218        struct hash_algo *algo;
 219        int ret;
 220        int i;
 221
 222        ret = hash_lookup_algo(algo_name, &algo);
 223        if (ret)
 224                return ret;
 225
 226        for (i = 0; i < algo->digest_size; i++) {
 227                char chr[3];
 228
 229                strncpy(chr, &str[i * 2], 2);
 230                result[i] = simple_strtoul(chr, NULL, 16);
 231        }
 232
 233        return 0;
 234}
 235
 236int hash_block(const char *algo_name, const void *data, unsigned int len,
 237               uint8_t *output, int *output_size)
 238{
 239        struct hash_algo *algo;
 240        int ret;
 241
 242        ret = hash_lookup_algo(algo_name, &algo);
 243        if (ret)
 244                return ret;
 245
 246        if (output_size && *output_size < algo->digest_size) {
 247                debug("Output buffer size %d too small (need %d bytes)",
 248                      *output_size, algo->digest_size);
 249                return -ENOSPC;
 250        }
 251        if (output_size)
 252                *output_size = algo->digest_size;
 253        algo->hash_func_ws(data, len, output, algo->chunk_size);
 254
 255        return 0;
 256}
 257
 258#if defined(CONFIG_CMD_HASH) || defined(CONFIG_CMD_SHA1SUM) || defined(CONFIG_CMD_CRC32)
 259/**
 260 * store_result: Store the resulting sum to an address or variable
 261 *
 262 * @algo:               Hash algorithm being used
 263 * @sum:                Hash digest (algo->digest_size bytes)
 264 * @dest:               Destination, interpreted as a hex address if it starts
 265 *                      with * (or allow_env_vars is 0) or otherwise as an
 266 *                      environment variable.
 267 * @allow_env_vars:     non-zero to permit storing the result to an
 268 *                      variable environment
 269 */
 270static void store_result(struct hash_algo *algo, const uint8_t *sum,
 271                         const char *dest, int allow_env_vars)
 272{
 273        unsigned int i;
 274        int env_var = 0;
 275
 276        /*
 277         * If environment variables are allowed, then we assume that 'dest'
 278         * is an environment variable, unless it starts with *, in which
 279         * case we assume it is an address. If not allowed, it is always an
 280         * address. This is to support the crc32 command.
 281         */
 282        if (allow_env_vars) {
 283                if (*dest == '*')
 284                        dest++;
 285                else
 286                        env_var = 1;
 287        }
 288
 289        if (env_var) {
 290                char str_output[HASH_MAX_DIGEST_SIZE * 2 + 1];
 291                char *str_ptr = str_output;
 292
 293                for (i = 0; i < algo->digest_size; i++) {
 294                        sprintf(str_ptr, "%02x", sum[i]);
 295                        str_ptr += 2;
 296                }
 297                *str_ptr = '\0';
 298                env_set(dest, str_output);
 299        } else {
 300                ulong addr;
 301                void *buf;
 302
 303                addr = simple_strtoul(dest, NULL, 16);
 304                buf = map_sysmem(addr, algo->digest_size);
 305                memcpy(buf, sum, algo->digest_size);
 306                unmap_sysmem(buf);
 307        }
 308}
 309
 310/**
 311 * parse_verify_sum: Parse a hash verification parameter
 312 *
 313 * @algo:               Hash algorithm being used
 314 * @verify_str:         Argument to parse. If it starts with * then it is
 315 *                      interpreted as a hex address containing the hash.
 316 *                      If the length is exactly the right number of hex digits
 317 *                      for the digest size, then we assume it is a hex digest.
 318 *                      Otherwise we assume it is an environment variable, and
 319 *                      look up its value (it must contain a hex digest).
 320 * @vsum:               Returns binary digest value (algo->digest_size bytes)
 321 * @allow_env_vars:     non-zero to permit storing the result to an environment
 322 *                      variable. If 0 then verify_str is assumed to be an
 323 *                      address, and the * prefix is not expected.
 324 * @return 0 if ok, non-zero on error
 325 */
 326static int parse_verify_sum(struct hash_algo *algo, char *verify_str,
 327                            uint8_t *vsum, int allow_env_vars)
 328{
 329        int env_var = 0;
 330
 331        /* See comment above in store_result() */
 332        if (allow_env_vars) {
 333                if (*verify_str == '*')
 334                        verify_str++;
 335                else
 336                        env_var = 1;
 337        }
 338
 339        if (!env_var) {
 340                ulong addr;
 341                void *buf;
 342
 343                addr = simple_strtoul(verify_str, NULL, 16);
 344                buf = map_sysmem(addr, algo->digest_size);
 345                memcpy(vsum, buf, algo->digest_size);
 346        } else {
 347                char *vsum_str;
 348                int digits = algo->digest_size * 2;
 349
 350                /*
 351                 * As with the original code from sha1sum.c, we assume that a
 352                 * string which matches the digest size exactly is a hex
 353                 * string and not an environment variable.
 354                 */
 355                if (strlen(verify_str) == digits)
 356                        vsum_str = verify_str;
 357                else {
 358                        vsum_str = env_get(verify_str);
 359                        if (vsum_str == NULL || strlen(vsum_str) != digits) {
 360                                printf("Expected %d hex digits in env var\n",
 361                                       digits);
 362                                return 1;
 363                        }
 364                }
 365
 366                hash_parse_string(algo->name, vsum_str, vsum);
 367        }
 368        return 0;
 369}
 370
 371static void hash_show(struct hash_algo *algo, ulong addr, ulong len, uint8_t *output)
 372{
 373        int i;
 374
 375        printf("%s for %08lx ... %08lx ==> ", algo->name, addr, addr + len - 1);
 376        for (i = 0; i < algo->digest_size; i++)
 377                printf("%02x", output[i]);
 378}
 379
 380int hash_command(const char *algo_name, int flags, cmd_tbl_t *cmdtp, int flag,
 381                 int argc, char * const argv[])
 382{
 383        ulong addr, len;
 384
 385        if ((argc < 2) || ((flags & HASH_FLAG_VERIFY) && (argc < 3)))
 386                return CMD_RET_USAGE;
 387
 388        addr = simple_strtoul(*argv++, NULL, 16);
 389        len = simple_strtoul(*argv++, NULL, 16);
 390
 391        if (multi_hash()) {
 392                struct hash_algo *algo;
 393                uint8_t output[HASH_MAX_DIGEST_SIZE];
 394                uint8_t vsum[HASH_MAX_DIGEST_SIZE];
 395                void *buf;
 396
 397                if (hash_lookup_algo(algo_name, &algo)) {
 398                        printf("Unknown hash algorithm '%s'\n", algo_name);
 399                        return CMD_RET_USAGE;
 400                }
 401                argc -= 2;
 402
 403                if (algo->digest_size > HASH_MAX_DIGEST_SIZE) {
 404                        puts("HASH_MAX_DIGEST_SIZE exceeded\n");
 405                        return 1;
 406                }
 407
 408                buf = map_sysmem(addr, len);
 409                algo->hash_func_ws(buf, len, output, algo->chunk_size);
 410                unmap_sysmem(buf);
 411
 412                /* Try to avoid code bloat when verify is not needed */
 413#if defined(CONFIG_CRC32_VERIFY) || defined(CONFIG_SHA1SUM_VERIFY) || \
 414        defined(CONFIG_HASH_VERIFY)
 415                if (flags & HASH_FLAG_VERIFY) {
 416#else
 417                if (0) {
 418#endif
 419                        if (parse_verify_sum(algo, *argv, vsum,
 420                                        flags & HASH_FLAG_ENV)) {
 421                                printf("ERROR: %s does not contain a valid "
 422                                        "%s sum\n", *argv, algo->name);
 423                                return 1;
 424                        }
 425                        if (memcmp(output, vsum, algo->digest_size) != 0) {
 426                                int i;
 427
 428                                hash_show(algo, addr, len, output);
 429                                printf(" != ");
 430                                for (i = 0; i < algo->digest_size; i++)
 431                                        printf("%02x", vsum[i]);
 432                                puts(" ** ERROR **\n");
 433                                return 1;
 434                        }
 435                } else {
 436                        hash_show(algo, addr, len, output);
 437                        printf("\n");
 438
 439                        if (argc) {
 440                                store_result(algo, output, *argv,
 441                                        flags & HASH_FLAG_ENV);
 442                        }
 443                }
 444
 445        /* Horrible code size hack for boards that just want crc32 */
 446        } else {
 447                ulong crc;
 448                ulong *ptr;
 449
 450                crc = crc32_wd(0, (const uchar *)addr, len, CHUNKSZ_CRC32);
 451
 452                printf("CRC32 for %08lx ... %08lx ==> %08lx\n",
 453                                addr, addr + len - 1, crc);
 454
 455                if (argc >= 3) {
 456                        ptr = (ulong *)simple_strtoul(argv[0], NULL, 16);
 457                        *ptr = crc;
 458                }
 459        }
 460
 461        return 0;
 462}
 463#endif /* CONFIG_CMD_HASH || CONFIG_CMD_SHA1SUM || CONFIG_CMD_CRC32) */
 464#endif /* !USE_HOSTCC */
 465