1/*
2 * Copyright 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <ctype.h>
8#include <getopt.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <string.h>
12
13#include "futility.h"
14
15static const char usage[] = "\n"
16	"Usage:  " MYNAME " %s [OPTIONS] DIGEST [...]\n"
17	"\n"
18	"This simulates a TPM PCR extension, to determine the expected output\n"
19	"\n"
20	"Each DIGEST arg should be a hex string (spaces optional) of the\n"
21	"appropriate length. The PCR is extended with each digest in turn\n"
22	"and the new value displayed.\n"
23	"\n"
24	"Options:\n"
25	"  -i      Initialize the PCR with the first DIGEST argument\n"
26	"            (the default is to start with all zeros)\n"
27	"  -2      Use sha256 DIGESTS (the default is sha1)\n"
28	"\n"
29	"Examples:\n"
30	"\n"
31	"  " MYNAME " %s b52791126f96a21a8ba4d511c6f25a1c1eb6dc9e\n"
32	"  " MYNAME " %s "
33	"'b5 27 91 12 6f 96 a2 1a 8b a4 d5 11 c6 f2 5a 1c 1e b6 dc 9e'\n"
34	"\n";
35
36static void help_and_quit(const char *prog)
37{
38	printf(usage, prog, prog, prog);
39}
40
41static int parse_hex(uint8_t *val, const char *str)
42{
43	uint8_t v = 0;
44	char c;
45	int digit;
46
47	for (digit = 0; digit < 2; digit++) {
48		c = *str;
49		if (!c)
50			return 0;
51		if (!isxdigit(c))
52			return 0;
53		c = tolower(c);
54		if (c >= '0' && c <= '9')
55			v += c - '0';
56		else
57			v += 10 + c - 'a';
58		if (!digit)
59			v <<= 4;
60		str++;
61	}
62
63	*val = v;
64	return 1;
65}
66
67static void parse_digest_or_die(uint8_t *buf, int len, const char *str)
68{
69	const char *s = str;
70	int i;
71
72	for (i = 0; i < len; i++) {
73		/* skip whitespace */
74		while (*s && isspace(*s))
75			s++;
76		if (!*s)
77			break;
78		if (!parse_hex(buf, s))
79			break;
80
81		/* on to the next byte */
82		s += 2;
83		buf++;
84	}
85
86	if (i != len) {
87		fprintf(stderr, "Invalid DIGEST \"%s\"\n", str);
88		exit(1);
89	}
90}
91
92static void print_digest(const uint8_t *buf, int len)
93{
94	int i;
95	for (i = 0; i < len; i++)
96		printf("%02x", buf[i]);
97}
98
99
100static int do_pcr(int argc, char *argv[])
101{
102	uint8_t accum[SHA256_DIGEST_SIZE * 2];
103	uint8_t pcr[SHA256_DIGEST_SIZE];
104	int digest_alg = SHA1_DIGEST_ALGORITHM;
105	int digest_size = SHA1_DIGEST_SIZE;
106	int opt_init = 0;
107	int errorcnt = 0;
108	uint8_t *digest;
109	int i;
110
111	opterr = 0;		/* quiet, you */
112	while ((i = getopt(argc, argv, ":i2")) != -1) {
113		switch (i) {
114		case 'i':
115			opt_init = 1;
116			break;
117		case '2':
118			digest_alg = SHA256_DIGEST_ALGORITHM;
119			digest_size = SHA256_DIGEST_SIZE;
120			break;
121		case '?':
122			if (optopt)
123				fprintf(stderr, "Unrecognized option: -%c\n",
124					optopt);
125			else
126				fprintf(stderr, "Unrecognized option\n");
127			errorcnt++;
128			break;
129		case ':':
130			fprintf(stderr, "Missing argument to -%c\n", optopt);
131			errorcnt++;
132			break;
133		default:
134			DIE;
135		}
136	}
137
138	if (errorcnt) {
139		help_and_quit(argv[0]);
140		return 1;
141	}
142
143	if (argc - optind < 1 + opt_init) {
144		fprintf(stderr, "You must extend at least one DIGEST\n");
145		help_and_quit(argv[0]);
146		return 1;
147	}
148
149	memset(pcr, 0, sizeof(pcr));
150
151	if (opt_init) {
152		parse_digest_or_die(pcr, digest_size, argv[optind]);
153		optind++;
154	}
155
156	printf("PCR: ");
157	print_digest(pcr, digest_size);
158	printf("\n");
159
160	for (i = optind; i < argc; i++) {
161		memcpy(accum, pcr, sizeof(pcr));
162		parse_digest_or_die(accum + digest_size, digest_size, argv[i]);
163
164		printf("   + ");
165		print_digest(accum + digest_size, digest_size);
166		printf("\n");
167
168		digest = DigestBuf(accum, digest_size * 2, digest_alg);
169		if (!digest) {
170			fprintf(stderr, "Error computing digest!\n");
171			return 1;
172		}
173		memcpy(pcr, digest, digest_size);
174		free(digest);
175
176		printf("PCR: ");
177		print_digest(pcr, digest_size);
178		printf("\n");
179	}
180
181	return 0;
182}
183
184DECLARE_FUTIL_COMMAND(pcr, do_pcr,
185		      VBOOT_VERSION_ALL,
186		      "Simulate a TPM PCR extension operation",
187		      help_and_quit);
188