hash.c 7.62 KB
Newer Older
Simon Glass's avatar
Simon Glass committed
1
2
3
4
5
6
7
8
9
/*
 * 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.
 *
10
 * SPDX-License-Identifier:	GPL-2.0+
Simon Glass's avatar
Simon Glass committed
11
12
13
14
 */

#include <common.h>
#include <command.h>
15
#include <hw_sha.h>
Simon Glass's avatar
Simon Glass committed
16
17
18
#include <hash.h>
#include <sha1.h>
#include <sha256.h>
19
#include <asm/io.h>
20
#include <asm/errno.h>
Simon Glass's avatar
Simon Glass committed
21
22
23

/*
 * These are the hash algorithms we support. Chips which support accelerated
24
25
 * crypto could perhaps add named version of these algorithms here. Note that
 * algorithm names must be in lower case.
Simon Glass's avatar
Simon Glass committed
26
27
 */
static struct hash_algo hash_algo[] = {
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	/*
	 * 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
45
46
47
48
49
50
	/*
	 * 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
Simon Glass's avatar
Simon Glass committed
51
	{
52
		"sha1",
Simon Glass's avatar
Simon Glass committed
53
54
55
56
		SHA1_SUM_LEN,
		sha1_csum_wd,
		CHUNKSZ_SHA1,
	},
57
#define MULTI_HASH
Simon Glass's avatar
Simon Glass committed
58
59
60
#endif
#ifdef CONFIG_SHA256
	{
61
		"sha256",
Simon Glass's avatar
Simon Glass committed
62
63
64
65
		SHA256_SUM_LEN,
		sha256_csum_wd,
		CHUNKSZ_SHA256,
	},
66
#define MULTI_HASH
Simon Glass's avatar
Simon Glass committed
67
#endif
68
	{
69
		"crc32",
70
71
72
73
		4,
		crc32_wd_buf,
		CHUNKSZ_CRC32,
	},
Simon Glass's avatar
Simon Glass committed
74
75
};

76
77
78
79
80
81
82
83
84
85
86
#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

Simon Glass's avatar
Simon Glass committed
87
88
89
90
91
92
/**
 * 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
93
94
95
96
 *			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
Simon Glass's avatar
Simon Glass committed
97
98
 */
static void store_result(struct hash_algo *algo, const u8 *sum,
99
			 const char *dest, int allow_env_vars)
Simon Glass's avatar
Simon Glass committed
100
101
{
	unsigned int i;
102
	int env_var = 0;
Simon Glass's avatar
Simon Glass committed
103

104
105
106
107
108
109
110
111
112
113
114
115
	/*
	 * 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;
	}
Simon Glass's avatar
Simon Glass committed
116

117
	if (env_var) {
Simon Glass's avatar
Simon Glass committed
118
119
120
121
122
123
124
125
126
		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);
127
	} else {
128
129
		ulong addr;
		void *buf;
130

131
132
133
134
		addr = simple_strtoul(dest, NULL, 16);
		buf = map_sysmem(addr, algo->digest_size);
		memcpy(buf, sum, algo->digest_size);
		unmap_sysmem(buf);
Simon Glass's avatar
Simon Glass committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
	}
}

/**
 * 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)
149
150
151
 * @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.
Simon Glass's avatar
Simon Glass committed
152
153
 * @return 0 if ok, non-zero on error
 */
154
155
static int parse_verify_sum(struct hash_algo *algo, char *verify_str, u8 *vsum,
			    int allow_env_vars)
Simon Glass's avatar
Simon Glass committed
156
{
157
158
159
160
161
162
163
164
165
166
167
	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) {
168
169
		ulong addr;
		void *buf;
Simon Glass's avatar
Simon Glass committed
170

171
172
173
		addr = simple_strtoul(verify_str, NULL, 16);
		buf = map_sysmem(addr, algo->digest_size);
		memcpy(vsum, buf, algo->digest_size);
Simon Glass's avatar
Simon Glass committed
174
175
176
177
178
179
180
181
182
183
184
185
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
	} 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++) {
212
		if (!strcmp(name, hash_algo[i].name))
Simon Glass's avatar
Simon Glass committed
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
			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]);
}

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
int hash_block(const char *algo_name, const void *data, unsigned int len,
	       uint8_t *output, int *output_size)
{
	struct hash_algo *algo;

	algo = find_hash_algo(algo_name);
	if (!algo) {
		debug("Unknown hash algorithm '%s'\n", algo_name);
		return -EPROTONOSUPPORT;
	}
	if (output_size && *output_size < algo->digest_size) {
		debug("Output buffer size %d too small (need %d bytes)",
		      *output_size, algo->digest_size);
		return -ENOSPC;
	}
	if (output_size)
		*output_size = algo->digest_size;
	algo->hash_func_ws(data, len, output, algo->chunk_size);

	return 0;
}

251
int hash_command(const char *algo_name, int flags, cmd_tbl_t *cmdtp, int flag,
Simon Glass's avatar
Simon Glass committed
252
253
254
255
256
257
258
		 int argc, char * const argv[])
{
	ulong addr, len;

	if (argc < 2)
		return CMD_RET_USAGE;

259
260
261
	addr = simple_strtoul(*argv++, NULL, 16);
	len = simple_strtoul(*argv++, NULL, 16);

262
263
264
265
	if (multi_hash()) {
		struct hash_algo *algo;
		u8 output[HASH_MAX_DIGEST_SIZE];
		u8 vsum[HASH_MAX_DIGEST_SIZE];
266
		void *buf;
Simon Glass's avatar
Simon Glass committed
267

268
269
270
271
272
273
274
275
276
277
278
		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;
		}
Simon Glass's avatar
Simon Glass committed
279

280
281
282
		buf = map_sysmem(addr, len);
		algo->hash_func_ws(buf, len, output, algo->chunk_size);
		unmap_sysmem(buf);
Simon Glass's avatar
Simon Glass committed
283

284
		/* Try to avoid code bloat when verify is not needed */
Simon Glass's avatar
Simon Glass committed
285
#ifdef CONFIG_HASH_VERIFY
286
		if (flags & HASH_FLAG_VERIFY) {
Simon Glass's avatar
Simon Glass committed
287
#else
288
		if (0) {
Simon Glass's avatar
Simon Glass committed
289
#endif
290
291
292
			if (!argc)
				return CMD_RET_USAGE;
			if (parse_verify_sum(algo, *argv, vsum,
293
					flags & HASH_FLAG_ENV)) {
294
295
296
297
298
299
				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;
Simon Glass's avatar
Simon Glass committed
300

301
302
303
304
305
306
307
308
				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 {
Simon Glass's avatar
Simon Glass committed
309
			show_hash(algo, addr, len, output);
310
311
312
313
314
315
			printf("\n");

			if (argc) {
				store_result(algo, output, *argv,
					flags & HASH_FLAG_ENV);
			}
Simon Glass's avatar
Simon Glass committed
316
		}
317
318

	/* Horrible code size hack for boards that just want crc32 */
Simon Glass's avatar
Simon Glass committed
319
	} else {
320
321
322
323
324
325
326
		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);
Simon Glass's avatar
Simon Glass committed
327

328
329
		if (argc >= 3) {
			ptr = (ulong *)simple_strtoul(argv[0], NULL, 16);
330
			*ptr = crc;
331
		}
Simon Glass's avatar
Simon Glass committed
332
333
334
335
	}

	return 0;
}