hash.c 7.7 KB
Newer Older
S
Simon Glass 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * 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>
28
#include <hw_sha.h>
S
Simon Glass 已提交
29 30 31
#include <hash.h>
#include <sha1.h>
#include <sha256.h>
32
#include <asm/io.h>
S
Simon Glass 已提交
33 34 35

/*
 * These are the hash algorithms we support. Chips which support accelerated
36 37
 * crypto could perhaps add named version of these algorithms here. Note that
 * algorithm names must be in lower case.
S
Simon Glass 已提交
38 39
 */
static struct hash_algo hash_algo[] = {
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
	/*
	 * CONFIG_SHA_HW_ACCEL is defined if hardware acceleration is
	 * available.
	 */
#ifdef CONFIG_SHA_HW_ACCEL
	{
		"sha1",
		SHA1_SUM_LEN,
		hw_sha1,
		CHUNKSZ_SHA1,
	}, {
		"sha256",
		SHA256_SUM_LEN,
		hw_sha256,
		CHUNKSZ_SHA256,
	},
#endif
57 58 59 60 61 62
	/*
	 * This is CONFIG_CMD_SHA1SUM instead of CONFIG_SHA1 since otherwise
	 * it bloats the code for boards which use SHA1 but not the 'hash'
	 * or 'sha1sum' commands.
	 */
#ifdef CONFIG_CMD_SHA1SUM
S
Simon Glass 已提交
63
	{
64
		"sha1",
S
Simon Glass 已提交
65 66 67 68
		SHA1_SUM_LEN,
		sha1_csum_wd,
		CHUNKSZ_SHA1,
	},
69
#define MULTI_HASH
S
Simon Glass 已提交
70 71 72
#endif
#ifdef CONFIG_SHA256
	{
73
		"sha256",
S
Simon Glass 已提交
74 75 76 77
		SHA256_SUM_LEN,
		sha256_csum_wd,
		CHUNKSZ_SHA256,
	},
78
#define MULTI_HASH
S
Simon Glass 已提交
79
#endif
80
	{
81
		"crc32",
82 83 84 85
		4,
		crc32_wd_buf,
		CHUNKSZ_CRC32,
	},
S
Simon Glass 已提交
86 87
};

88 89 90 91 92 93 94 95 96 97 98
#if defined(CONFIG_HASH_VERIFY) || defined(CONFIG_CMD_HASH)
#define MULTI_HASH
#endif

/* Try to minimize code size for boards that don't want much hashing */
#ifdef MULTI_HASH
#define multi_hash()	1
#else
#define multi_hash()	0
#endif

S
Simon Glass 已提交
99 100 101 102 103 104
/**
 * 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
105 106 107 108
 *			with * (or allow_env_vars is 0) or otherwise as an
 *			environment variable.
 * @allow_env_vars:	non-zero to permit storing the result to an
 *			variable environment
S
Simon Glass 已提交
109 110
 */
static void store_result(struct hash_algo *algo, const u8 *sum,
111
			 const char *dest, int allow_env_vars)
S
Simon Glass 已提交
112 113
{
	unsigned int i;
114
	int env_var = 0;
S
Simon Glass 已提交
115

116 117 118 119 120 121 122 123 124 125 126 127
	/*
	 * If environment variables are allowed, then we assume that 'dest'
	 * is an environment variable, unless it starts with *, in which
	 * case we assume it is an address. If not allowed, it is always an
	 * address. This is to support the crc32 command.
	 */
	if (allow_env_vars) {
		if (*dest == '*')
			dest++;
		else
			env_var = 1;
	}
S
Simon Glass 已提交
128

129
	if (env_var) {
S
Simon Glass 已提交
130 131 132 133 134 135 136 137 138
		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);
139
	} else {
140 141
		ulong addr;
		void *buf;
142

143 144 145 146
		addr = simple_strtoul(dest, NULL, 16);
		buf = map_sysmem(addr, algo->digest_size);
		memcpy(buf, sum, algo->digest_size);
		unmap_sysmem(buf);
S
Simon Glass 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160
	}
}

/**
 * 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)
161 162 163
 * @allow_env_vars:	non-zero to permit storing the result to an environment
 *			variable. If 0 then verify_str is assumed to be an
 *			address, and the * prefix is not expected.
S
Simon Glass 已提交
164 165
 * @return 0 if ok, non-zero on error
 */
166 167
static int parse_verify_sum(struct hash_algo *algo, char *verify_str, u8 *vsum,
			    int allow_env_vars)
S
Simon Glass 已提交
168
{
169 170 171 172 173 174 175 176 177 178 179
	int env_var = 0;

	/* See comment above in store_result() */
	if (allow_env_vars) {
		if (*verify_str == '*')
			verify_str++;
		else
			env_var = 1;
	}

	if (env_var) {
180 181
		ulong addr;
		void *buf;
S
Simon Glass 已提交
182

183 184 185
		addr = simple_strtoul(verify_str, NULL, 16);
		buf = map_sysmem(addr, algo->digest_size);
		memcpy(vsum, buf, algo->digest_size);
S
Simon Glass 已提交
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	} 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++) {
224
		if (!strcmp(name, hash_algo[i].name))
S
Simon Glass 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
			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]);
}

241
int hash_command(const char *algo_name, int flags, cmd_tbl_t *cmdtp, int flag,
S
Simon Glass 已提交
242 243 244 245 246 247 248
		 int argc, char * const argv[])
{
	ulong addr, len;

	if (argc < 2)
		return CMD_RET_USAGE;

249 250 251
	addr = simple_strtoul(*argv++, NULL, 16);
	len = simple_strtoul(*argv++, NULL, 16);

252 253 254 255
	if (multi_hash()) {
		struct hash_algo *algo;
		u8 output[HASH_MAX_DIGEST_SIZE];
		u8 vsum[HASH_MAX_DIGEST_SIZE];
256
		void *buf;
S
Simon Glass 已提交
257

258 259 260 261 262 263 264 265 266 267 268
		algo = find_hash_algo(algo_name);
		if (!algo) {
			printf("Unknown hash algorithm '%s'\n", algo_name);
			return CMD_RET_USAGE;
		}
		argc -= 2;

		if (algo->digest_size > HASH_MAX_DIGEST_SIZE) {
			puts("HASH_MAX_DIGEST_SIZE exceeded\n");
			return 1;
		}
S
Simon Glass 已提交
269

270 271 272
		buf = map_sysmem(addr, len);
		algo->hash_func_ws(buf, len, output, algo->chunk_size);
		unmap_sysmem(buf);
S
Simon Glass 已提交
273

274
		/* Try to avoid code bloat when verify is not needed */
S
Simon Glass 已提交
275
#ifdef CONFIG_HASH_VERIFY
276
		if (flags & HASH_FLAG_VERIFY) {
S
Simon Glass 已提交
277
#else
278
		if (0) {
S
Simon Glass 已提交
279
#endif
280 281 282
			if (!argc)
				return CMD_RET_USAGE;
			if (parse_verify_sum(algo, *argv, vsum,
283
					flags & HASH_FLAG_ENV)) {
284 285 286 287 288 289
				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;
S
Simon Glass 已提交
290

291 292 293 294 295 296 297 298
				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 {
S
Simon Glass 已提交
299
			show_hash(algo, addr, len, output);
300 301 302 303 304 305
			printf("\n");

			if (argc) {
				store_result(algo, output, *argv,
					flags & HASH_FLAG_ENV);
			}
S
Simon Glass 已提交
306
		}
307 308

	/* Horrible code size hack for boards that just want crc32 */
S
Simon Glass 已提交
309
	} else {
310 311 312 313 314 315 316
		ulong crc;
		ulong *ptr;

		crc = crc32_wd(0, (const uchar *)addr, len, CHUNKSZ_CRC32);

		printf("CRC32 for %08lx ... %08lx ==> %08lx\n",
				addr, addr + len - 1, crc);
S
Simon Glass 已提交
317

318 319 320
		if (argc > 3) {
			ptr = (ulong *)simple_strtoul(argv[3], NULL, 16);
			*ptr = crc;
321
		}
S
Simon Glass 已提交
322 323 324 325
	}

	return 0;
}