6ca6ba6ef604e8b9bb86edc400bc37e43320fd9a
[vboot.git] / futility / cmd_vbutil_keyblock.c
1 /* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  *
5  * Verified boot key block utility
6  */
7
8 #include <getopt.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "cryptolib.h"
15 #include "futility.h"
16 #include "host_common.h"
17 #include "util_misc.h"
18 #include "vb1_helper.h"
19 #include "vb2_common.h"
20 #include "vboot_common.h"
21
22 /* Command line options */
23 enum {
24         OPT_MODE_PACK = 1000,
25         OPT_MODE_UNPACK,
26         OPT_DATAPUBKEY,
27         OPT_SIGNPUBKEY,
28         OPT_SIGNPRIVATE,
29         OPT_SIGNPRIVATE_PEM,
30         OPT_PEM_ALGORITHM,
31         OPT_EXTERNAL_SIGNER,
32         OPT_FLAGS,
33         OPT_HELP,
34 };
35
36 static const struct option long_opts[] = {
37         {"pack", 1, 0, OPT_MODE_PACK},
38         {"unpack", 1, 0, OPT_MODE_UNPACK},
39         {"datapubkey", 1, 0, OPT_DATAPUBKEY},
40         {"signpubkey", 1, 0, OPT_SIGNPUBKEY},
41         {"signprivate", 1, 0, OPT_SIGNPRIVATE},
42         {"signprivate_pem", 1, 0, OPT_SIGNPRIVATE_PEM},
43         {"pem_algorithm", 1, 0, OPT_PEM_ALGORITHM},
44         {"externalsigner", 1, 0, OPT_EXTERNAL_SIGNER},
45         {"flags", 1, 0, OPT_FLAGS},
46         {"help", 0, 0, OPT_HELP},
47         {NULL, 0, 0, 0}
48 };
49
50 static const char usage[] =
51         "\n"
52         "Usage:  " MYNAME " %s <--pack|--unpack> <file> [OPTIONS]\n"
53         "\n"
54         "For '--pack <file>', required OPTIONS are:\n"
55         "  --datapubkey <file>         Data public key in .vbpubk format\n"
56         "\n"
57         "Optional OPTIONS are:\n"
58         "  --signprivate <file>"
59         "        Signing private key in .vbprivk format.\n"
60         "OR\n"
61         "  --signprivate_pem <file>\n"
62         "  --pem_algorithm <algo>\n"
63         "        Signing private key in .pem format and algorithm id.\n"
64         "(If one of the above arguments is not specified, the keyblock will\n"
65         "not be signed.)\n"
66         "\n"
67         "  --flags <number>            Specifies allowed use conditions.\n"
68         "  --externalsigner \"cmd\""
69         "        Use an external program cmd to calculate the signatures.\n"
70         "\n"
71         "For '--unpack <file>', optional OPTIONS are:\n"
72         "  --signpubkey <file>"
73         "        Signing public key in .vbpubk format. This is required to\n"
74         "                                verify a signed keyblock.\n"
75         "  --datapubkey <file>"
76         "        Write the data public key to this file.\n\n";
77
78 static void print_help(int argc, char *argv[])
79 {
80         printf(usage, argv[0]);
81 }
82
83 /* Pack a .keyblock */
84 static int Pack(const char *outfile, const char *datapubkey,
85                 const char *signprivate,
86                 const char *signprivate_pem, uint64_t pem_algorithm,
87                 uint64_t flags, const char *external_signer)
88 {
89         VbPublicKey *data_key;
90         VbPrivateKey *signing_key = NULL;
91         VbKeyBlockHeader *block;
92
93         if (!outfile) {
94                 fprintf(stderr,
95                         "vbutil_keyblock: Must specify output filename.\n");
96                 return 1;
97         }
98         if (!datapubkey) {
99                 fprintf(stderr,
100                         "vbutil_keyblock: Must specify data public key.\n");
101                 return 1;
102         }
103
104         data_key = PublicKeyRead(datapubkey);
105         if (!data_key) {
106                 fprintf(stderr, "vbutil_keyblock: Error reading data key.\n");
107                 return 1;
108         }
109
110         if (signprivate_pem) {
111                 if (pem_algorithm >= kNumAlgorithms) {
112                         fprintf(stderr,
113                                 "vbutil_keyblock: Invalid --pem_algorithm %"
114                                 PRIu64 "\n", pem_algorithm);
115                         return 1;
116                 }
117                 if (external_signer) {
118                         /* External signing uses the PEM file directly. */
119                         block = KeyBlockCreate_external(data_key,
120                                                         signprivate_pem,
121                                                         pem_algorithm, flags,
122                                                         external_signer);
123                 } else {
124                         signing_key =
125                             PrivateKeyReadPem(signprivate_pem, pem_algorithm);
126                         if (!signing_key) {
127                                 fprintf(stderr, "vbutil_keyblock:"
128                                         " Error reading signing key.\n");
129                                 return 1;
130                         }
131                         block = KeyBlockCreate(data_key, signing_key, flags);
132                 }
133         } else {
134                 if (signprivate) {
135                         signing_key = PrivateKeyRead(signprivate);
136                         if (!signing_key) {
137                                 fprintf(stderr, "vbutil_keyblock:"
138                                         " Error reading signing key.\n");
139                                 return 1;
140                         }
141                 }
142                 block = KeyBlockCreate(data_key, signing_key, flags);
143         }
144
145         free(data_key);
146         if (signing_key)
147                 free(signing_key);
148
149         if (0 != KeyBlockWrite(outfile, block)) {
150                 fprintf(stderr, "vbutil_keyblock: Error writing key block.\n");
151                 return 1;
152         }
153         free(block);
154         return 0;
155 }
156
157 static int Unpack(const char *infile, const char *datapubkey,
158                   const char *signpubkey)
159 {
160         VbPublicKey *data_key;
161         VbPublicKey *sign_key = NULL;
162         VbKeyBlockHeader *block;
163
164         if (!infile) {
165                 fprintf(stderr, "vbutil_keyblock: Must specify filename\n");
166                 return 1;
167         }
168
169         block = KeyBlockRead(infile);
170         if (!block) {
171                 fprintf(stderr, "vbutil_keyblock: Error reading key block.\n");
172                 return 1;
173         }
174
175         /* If the block is signed, then verify it with the signing public key,
176          * since KeyBlockRead() only verified the hash. */
177         if (block->key_block_signature.sig_size && signpubkey) {
178                 sign_key = PublicKeyRead(signpubkey);
179                 if (!sign_key) {
180                         fprintf(stderr,
181                                 "vbutil_keyblock: Error reading signpubkey.\n");
182                         return 1;
183                 }
184                 if (0 !=
185                     KeyBlockVerify(block, block->key_block_size, sign_key, 0)) {
186                         fprintf(stderr, "vbutil_keyblock:"
187                                 " Error verifying key block.\n");
188                         return 1;
189                 }
190                 free(sign_key);
191         }
192
193         printf("Key block file:       %s\n", infile);
194         printf("Signature             %s\n", sign_key ? "valid" : "ignored");
195         printf("Flags:                %" PRIu64 " ", block->key_block_flags);
196         if (block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0)
197                 printf(" !DEV");
198         if (block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1)
199                 printf(" DEV");
200         if (block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)
201                 printf(" !REC");
202         if (block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)
203                 printf(" REC");
204         printf("\n");
205
206         data_key = &block->data_key;
207         printf("Data key algorithm:   %" PRIu64 " %s\n", data_key->algorithm,
208                vb1_crypto_name(data_key->algorithm));
209         printf("Data key version:     %" PRIu64 "\n", data_key->key_version);
210         printf("Data key sha1sum:     %s\n",
211                packed_key_sha1_string((struct vb2_packed_key *)data_key));
212
213         if (datapubkey) {
214                 if (0 != PublicKeyWrite(datapubkey, data_key)) {
215                         fprintf(stderr, "vbutil_keyblock:"
216                                 " unable to write public key\n");
217                         return 1;
218                 }
219         }
220
221         free(block);
222         return 0;
223 }
224
225 static int do_vbutil_keyblock(int argc, char *argv[])
226 {
227
228         char *filename = NULL;
229         char *datapubkey = NULL;
230         char *signpubkey = NULL;
231         char *signprivate = NULL;
232         char *signprivate_pem = NULL;
233         char *external_signer = NULL;
234         uint64_t flags = 0;
235         uint64_t pem_algorithm = 0;
236         int is_pem_algorithm = 0;
237         int mode = 0;
238         int parse_error = 0;
239         char *e;
240         int i;
241
242         while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
243                 switch (i) {
244                 case '?':
245                         /* Unhandled option */
246                         printf("Unknown option\n");
247                         parse_error = 1;
248                         break;
249                 case OPT_HELP:
250                         print_help(argc, argv);
251                         return !!parse_error;
252
253                 case OPT_MODE_PACK:
254                 case OPT_MODE_UNPACK:
255                         mode = i;
256                         filename = optarg;
257                         break;
258
259                 case OPT_DATAPUBKEY:
260                         datapubkey = optarg;
261                         break;
262
263                 case OPT_SIGNPUBKEY:
264                         signpubkey = optarg;
265                         break;
266
267                 case OPT_SIGNPRIVATE:
268                         signprivate = optarg;
269                         break;
270
271                 case OPT_SIGNPRIVATE_PEM:
272                         signprivate_pem = optarg;
273                         break;
274
275                 case OPT_PEM_ALGORITHM:
276                         pem_algorithm = strtoul(optarg, &e, 0);
277                         if (!*optarg || (e && *e)) {
278                                 fprintf(stderr, "Invalid --pem_algorithm\n");
279                                 parse_error = 1;
280                         } else {
281                                 is_pem_algorithm = 1;
282                         }
283                         break;
284
285                 case OPT_EXTERNAL_SIGNER:
286                         external_signer = optarg;
287                         break;
288
289                 case OPT_FLAGS:
290                         flags = strtoul(optarg, &e, 0);
291                         if (!*optarg || (e && *e)) {
292                                 fprintf(stderr, "Invalid --flags\n");
293                                 parse_error = 1;
294                         }
295                         break;
296                 }
297         }
298
299         /* Check if the right combination of options was provided. */
300         if (signprivate && signprivate_pem) {
301                 fprintf(stderr,
302                         "Only one of --signprivate or --signprivate_pem must"
303                         " be specified\n");
304                 parse_error = 1;
305         }
306
307         if (signprivate_pem && !is_pem_algorithm) {
308                 fprintf(stderr, "--pem_algorithm must be used with"
309                         " --signprivate_pem\n");
310                 parse_error = 1;
311         }
312
313         if (external_signer && !signprivate_pem) {
314                 fprintf(stderr,
315                         "--externalsigner must be used with --signprivate_pem"
316                         "\n");
317                 parse_error = 1;
318         }
319
320         if (parse_error) {
321                 print_help(argc, argv);
322                 return 1;
323         }
324
325         switch (mode) {
326         case OPT_MODE_PACK:
327                 return Pack(filename, datapubkey, signprivate,
328                             signprivate_pem, pem_algorithm,
329                             flags, external_signer);
330         case OPT_MODE_UNPACK:
331                 return Unpack(filename, datapubkey, signpubkey);
332         default:
333                 printf("Must specify a mode.\n");
334                 print_help(argc, argv);
335                 return 1;
336         }
337 }
338
339 DECLARE_FUTIL_COMMAND(vbutil_keyblock, do_vbutil_keyblock, VBOOT_VERSION_1_0,
340                       "Creates, signs, and verifies a keyblock");