1 /*############################################################################
2 # Copyright 2016-2017 Intel Corporation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 ############################################################################*/
16
17 /*!
18 * \file
19 *
20 * \brief Extract member private keys from key output file
21 *
22 * Not validating SHA hashes in key file
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include <argtable3.h>
29 #include "epid/common/types.h"
30 #include "util/buffutil.h"
31 #include "util/envutil.h"
32 #include "util/stdtypes.h"
33 #include "util/strutil.h"
34
35 #define PROGRAM_NAME "extractkeys"
36 #define ARGPARSE_ERROR_MAX 20
37 #define ARGTABLE_SIZE 6
38
39 #pragma pack(1)
40 /// Intel(R) EPID Key Output File Entry
41 typedef struct EpidKeyOutputFileKey {
42 unsigned char product_id[2]; ///< 2-byte Product ID (Big Endian)
43 unsigned char key_id[8]; ///< 8-byte Key Unique Id(Big Endian)
44 unsigned char svn[4]; ///< 4-byte Security Version Number (SVN) (Big Endian)
45 PrivKey privkey; ///< Intel(R) EPID 2.0 Private Key
46 unsigned char hash[20]; ///< 20-byte SHA-1 of above
47 } EpidKeyOutputFileKey;
48
49 /// Intel(R) EPID Compressed Key Output File Entry
50 typedef struct EpidCompressedKeyOutputFileKey {
51 unsigned char product_id[2]; ///< 2-byte Product ID (Big Endian)
52 unsigned char key_id[8]; ///< 8-byte Key Unique Id(Big Endian)
53 unsigned char svn[4]; ///< 4-byte Security Version Number (SVN) (Big Endian)
54 CompressedPrivKey privkey; ///< Intel(R) EPID 2.0 Compressed Private Key
55 unsigned char hash[20]; ///< 20-byte SHA-1 of above
56 } EpidCompressedKeyOutputFileKey;
57 #pragma pack()
58
59 /// Main entrypoint
main(int argc,char * argv[])60 int main(int argc, char* argv[]) {
61 // intermediate return value for C style functions
62 int ret_value = EXIT_SUCCESS;
63 // Buffer to store read key
64 uint8_t temp[sizeof(EpidKeyOutputFileKey)] = {0};
65
66 // Private key to extract
67 void* privkey = 0;
68 size_t privkey_size = 0;
69
70 size_t keyfile_size = 0;
71 size_t keyfile_entry_size = 0;
72 size_t num_keys_extracted = 0;
73 size_t num_keys_in_file = 0;
74
75 FILE* file = NULL;
76
77 // Verbose flag parameter
78 static bool verbose_flag = false;
79
80 int i = 0;
81 size_t bytes_read = 0;
82
83 struct arg_file* keyfile =
84 arg_file1(NULL, NULL, "FILE", "FILE containing keys to extract");
85 struct arg_int* num_keys_to_extract =
86 arg_int1(NULL, NULL, "NUM", "number of keys to extract");
87 struct arg_lit* compressed =
88 arg_lit0("c", "compressed", "extract compressed keys");
89 struct arg_lit* help = arg_lit0(NULL, "help", "display this help and exit");
90 struct arg_lit* verbose =
91 arg_lit0("v", "verbose", "print status messages to stdout");
92 struct arg_end* end = arg_end(ARGPARSE_ERROR_MAX);
93 void* argtable[ARGTABLE_SIZE];
94 int nerrors;
95
96 /* initialize the argtable array with ptrs to the arg_xxx structures
97 * constructed above */
98 argtable[0] = keyfile;
99 argtable[1] = num_keys_to_extract;
100 argtable[2] = compressed;
101 argtable[3] = help;
102 argtable[4] = verbose;
103 argtable[5] = end;
104
105 // set program name for logging
106 set_prog_name(PROGRAM_NAME);
107 do {
108 /* verify the argtable[] entries were allocated sucessfully */
109 if (arg_nullcheck(argtable) != 0) {
110 /* NULL entries were detected, some allocations must have failed */
111 printf("%s: insufficient memory\n", PROGRAM_NAME);
112 ret_value = EXIT_FAILURE;
113 break;
114 }
115
116 /* Parse the command line as defined by argtable[] */
117 nerrors = arg_parse(argc, argv, argtable);
118
119 if (help->count > 0) {
120 log_fmt(
121 "Usage: %s [OPTION]... [FILE] [NUM]\n"
122 "Extract the first NUM private keys from FILE to current "
123 "directory.\n"
124 "\n"
125 "Options:\n",
126 PROGRAM_NAME);
127 arg_print_glossary(stdout, argtable, " %-25s %s\n");
128 ret_value = EXIT_SUCCESS;
129 break;
130 }
131 if (verbose->count > 0) {
132 verbose_flag = ToggleVerbosity();
133 }
134 /* If the parser returned any errors then display them and exit */
135 if (nerrors > 0) {
136 /* Display the error details contained in the arg_end struct.*/
137 arg_print_errors(stderr, end, PROGRAM_NAME);
138 fprintf(stderr, "Try '%s --help' for more information.\n", PROGRAM_NAME);
139 ret_value = EXIT_FAILURE;
140 break;
141 }
142
143 if (num_keys_to_extract->ival[0] < 0) {
144 log_error("unable extract negative number of keys");
145 ret_value = EXIT_FAILURE;
146 break;
147 }
148
149 // check file existence
150 if (!FileExists(keyfile->filename[0])) {
151 log_error("cannot access '%s'", keyfile->filename[0]);
152 ret_value = EXIT_FAILURE;
153 break;
154 }
155
156 keyfile_size = GetFileSize(keyfile->filename[0]);
157 if (compressed->count > 0) {
158 privkey_size = sizeof(CompressedPrivKey);
159 privkey = &(((EpidCompressedKeyOutputFileKey*)&temp[0])->privkey);
160 keyfile_entry_size = sizeof(EpidCompressedKeyOutputFileKey);
161 } else {
162 privkey_size = sizeof(PrivKey);
163 privkey = &(((EpidKeyOutputFileKey*)&temp[0])->privkey);
164 keyfile_entry_size = sizeof(EpidKeyOutputFileKey);
165 }
166
167 if (0 != keyfile_size % keyfile_entry_size) {
168 log_error(
169 "input file '%s' is invalid: does not contain integral number of "
170 "keys",
171 keyfile->filename[0]);
172 ret_value = EXIT_FAILURE;
173 break;
174 }
175 num_keys_in_file = keyfile_size / keyfile_entry_size;
176
177 if ((unsigned int)num_keys_to_extract->ival[0] > num_keys_in_file) {
178 log_error("can not extract %d keys: only %d in file",
179 num_keys_to_extract->ival[0], num_keys_in_file);
180 ret_value = EXIT_FAILURE;
181 break;
182 }
183
184 file = fopen(keyfile->filename[0], "rb");
185 if (!file) {
186 log_error("failed read from '%s'", keyfile->filename[0]);
187
188 ret_value = EXIT_FAILURE;
189 break;
190 }
191
192 // start extraction
193 for (i = 0; i < num_keys_to_extract->ival[0]; ++i) {
194 int seek_failed = 0;
195 seek_failed = fseek(file, (int)(i * keyfile_entry_size), SEEK_SET);
196 bytes_read = fread(&temp, 1, keyfile_entry_size, file);
197 if (seek_failed || bytes_read != keyfile_entry_size) {
198 log_error("failed to extract key #%lu from '%s'", i,
199 keyfile->filename[0]);
200 } else {
201 char outkeyname[256] = {0};
202 snprintf(outkeyname, sizeof(outkeyname), "mprivkey%010u.dat", i);
203
204 if (FileExists(outkeyname)) {
205 log_error("file '%s' already exists", outkeyname);
206 ret_value = EXIT_FAILURE;
207 break;
208 }
209 if (0 != WriteLoud(privkey, privkey_size, outkeyname)) {
210 log_error("failed to write key #%lu from '%s'", i,
211 keyfile->filename[0]);
212 } else {
213 num_keys_extracted++;
214 }
215 }
216 }
217 if (EXIT_FAILURE == ret_value) {
218 break;
219 }
220
221 log_msg("extracted %lu of %lu keys", num_keys_extracted, num_keys_in_file);
222 } while (0);
223
224 if (file) {
225 fclose(file);
226 file = NULL;
227 }
228
229 arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
230
231 return ret_value;
232 }
233