xref: /netbsd-src/external/bsd/elftosb/dist/encryptgpk/encryptgpk.cpp (revision 993229b6fea628ff8b1fa09146c80b0cfb2768eb)
1 /*
2  * File:	encryptgpk.cpp
3  *
4  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5  * See included license file for license details.
6  */
7 
8 #include "stdafx.h"
9 #include <iostream>
10 #include <fstream>
11 #include <sstream>
12 #include <stdlib.h>
13 #include <stdexcept>
14 #include <stdio.h>
15 #include "options.h"
16 #include "EncoreBootImage.h"
17 #include "smart_ptr.h"
18 #include "Logging.h"
19 #include "format_string.h"
20 #include "Blob.h"
21 #include "Random.h"
22 #include "rijndael.h"
23 
24 using namespace elftosb;
25 
26 //! Size in bytes of the unencrypted group private key.
27 #define GPK_LENGTH (40)
28 
29 //! Size in bytes of the encrypted output data. This size must be modulo 16, the chunk size for the
30 //! AES-128 crypto algorithm. The group private key is inserted at offset 16.
31 #define OUTPUT_DATA_LENGTH (64)
32 
33 //! Position in the output data of the first byte of the group private key.
34 #define OUTPUT_DATA_GPK_OFFSET (16)
35 
36 //! The tool's name.
37 const char k_toolName[] = "encryptgpk";
38 
39 //! Current version number for the tool.
40 const char k_version[] = "1.0.2";
41 
42 //! Copyright string.
43 const char k_copyright[] = "Copyright (c) 2008 Freescale Semiconductor. All rights reserved.";
44 
45 //! Default output array name.
46 const char k_defaultArrayName[] = "_endDisplay";
47 
48 //! Definition of command line options.
49 static const char * k_optionsDefinition[] = {
50 	"?|help",
51 	"v|version",
52 	"k:key <file>",
53 	"z|zero-key",
54 	"o:output",
55 	"p:prefix",
56 	"a:array",
57 	"d|debug",
58 	"q|quiet",
59 	"V|verbose",
60 	NULL
61 };
62 
63 //! Help string.
64 const char k_usageText[] = "\nOptions:\n\
65   -?/--help                    Show this help\n\
66   -v/--version                 Display tool version\n\
67   -k/--key <file>              Add OTP key used for decryption\n\
68   -z/--zero-key                Add default key of all zeroes\n\
69   -o/--output <file>           Write output to this file\n\
70   -p/--prefix <prefix>         Set the output array prefix\n\
71   -a/--array <name>            Specify the output array name\n\
72   -d/--debug                   Enable debug output\n\
73   -q/--quiet                   Output only warnings and errors\n\
74   -V/--verbose                 Print extra detailed log information\n\n";
75 
76 //! Init vector used for CBC encrypting the output data.
77 static const uint8_t kInitVector[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
78 
79 //! An array of strings.
80 typedef std::vector<std::string> string_vector_t;
81 
82 // prototypes
83 int main(int argc, char* argv[], char* envp[]);
84 
85 /*!
86  * \brief Class that encapsulates the sbtool interface.
87  *
88  * A single global logger instance is created during object construction. It is
89  * never freed because we need it up to the last possible minute, when an
90  * exception could be thrown.
91  */
92 class encryptgpk
93 {
94 protected:
95 	int m_argc;							//!< Number of command line arguments.
96 	char ** m_argv;						//!< String value for each command line argument.
97 	StdoutLogger * m_logger;			//!< Singleton logger instance.
98 	string_vector_t m_keyFilePaths;		//!< Paths to OTP key files.
99 	string_vector_t m_positionalArgs;	//!< Arguments coming after explicit options.
100 	bool m_isVerbose;					//!< Whether the verbose flag was turned on.
101 	bool m_useDefaultKey;					//!< Include a default (zero) crypto key.
102 	std::string m_outputPath;			//!< Path to output file.
103 	std::string m_gpkPath;				//!< Path to input group private key file.
104 	std::string m_outputPrefix;			//!< Prefix to the output array.
105 	std::string m_arrayName;			//!< Output array's name.
106 
107 public:
108 	/*!
109 	 * Constructor.
110 	 *
111 	 * Creates the singleton logger instance.
112 	 */
encryptgpk(int argc,char * argv[])113 	encryptgpk(int argc, char * argv[])
114 	:	m_argc(argc),
115 		m_argv(argv),
116 		m_logger(0),
117 		m_keyFilePaths(),
118 		m_positionalArgs(),
119 		m_isVerbose(false),
120 		m_useDefaultKey(false),
121 		m_outputPath(),
122 		m_gpkPath(),
123 		m_outputPrefix(),
124 		m_arrayName(k_defaultArrayName)
125 	{
126 		// create logger instance
127 		m_logger = new StdoutLogger();
128 		m_logger->setFilterLevel(Logger::INFO);
129 		Log::setLogger(m_logger);
130 	}
131 
132 	/*!
133 	 * Destructor.
134 	 */
~encryptgpk()135 	~encryptgpk()
136 	{
137 	}
138 
139 	/*!
140 	 * Reads the command line options passed into the constructor.
141 	 *
142 	 * This method can return a return code to its caller, which will cause the
143 	 * tool to exit immediately with that return code value. Normally, though, it
144 	 * will return -1 to signal that the tool should continue to execute and
145 	 * all options were processed successfully.
146 	 *
147 	 * The Options class is used to parse command line options. See
148 	 * #k_optionsDefinition for the list of options and #k_usageText for the
149 	 * descriptive help for each option.
150 	 *
151 	 * \retval -1 The options were processed successfully. Let the tool run normally.
152 	 * \return A zero or positive result is a return code value that should be
153 	 *		returned from the tool as it exits immediately.
154 	 */
processOptions()155 	int processOptions()
156 	{
157 		Options options(*m_argv, k_optionsDefinition);
158 		OptArgvIter iter(--m_argc, ++m_argv);
159 
160 		// process command line options
161 		int optchar;
162 		const char * optarg;
163 		while (optchar = options(iter, optarg))
164 		{
165 			switch (optchar)
166 			{
167 				case '?':
168 					printUsage(options);
169 					return 0;
170 
171 				case 'v':
172 					printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
173 					return 0;
174 
175 				case 'k':
176 					m_keyFilePaths.push_back(optarg);
177 					break;
178 
179 				case 'z':
180 					m_useDefaultKey = true;
181 					break;
182 
183 				case 'o':
184 					m_outputPath = optarg;
185 					break;
186 
187 				case 'p':
188 					m_outputPrefix = optarg;
189 					break;
190 
191 				case 'a':
192 					m_arrayName = optarg;
193 					break;
194 
195 				case 'd':
196 					Log::getLogger()->setFilterLevel(Logger::DEBUG);
197 					break;
198 
199 				case 'q':
200 					Log::getLogger()->setFilterLevel(Logger::WARNING);
201 					break;
202 
203 				case 'V':
204 					m_isVerbose = true;
205 					break;
206 
207 				default:
208 					Log::log(Logger::ERROR, "error: unrecognized option\n\n");
209 					printUsage(options);
210 					return 1;
211 			}
212 		}
213 
214 		// handle positional args
215 		if (iter.index() < m_argc)
216 		{
217 //			Log::SetOutputLevel leveler(Logger::DEBUG);
218 //			Log::log("positional args:\n");
219 			int i;
220 			for (i = iter.index(); i < m_argc; ++i)
221 			{
222 //				Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
223 				m_positionalArgs.push_back(m_argv[i]);
224 			}
225 		}
226 
227 		// all is well
228 		return -1;
229 	}
230 
231 	/*!
232 	 * Prints help for the tool.
233 	 */
printUsage(Options & options)234 	void printUsage(Options & options)
235 	{
236 		options.usage(std::cout, "gpk-file");
237 		printf(k_usageText, k_toolName);
238 	}
239 
240 	/*!
241 	 * Core of the tool. Calls processOptions() to handle command line options
242 	 * before performing the real work the tool does.
243 	 */
run()244 	int run()
245 	{
246 		try
247 		{
248 			// read command line options
249 			int result;
250 			if ((result = processOptions()) != -1)
251 			{
252 				return result;
253 			}
254 
255 			// set verbose logging
256 			setVerboseLogging();
257 
258 			// make sure a file was provided
259 			if (m_positionalArgs.size() < 1)
260 			{
261 				throw std::runtime_error("no input file path was provided");
262 			}
263 
264 			// Make sure at least one key was specified.
265 			if (m_keyFilePaths.size() == 0 && m_useDefaultKey == false)
266 			{
267 				throw std::runtime_error("no crypto key was specified");
268 			}
269 
270 			// Do the work.
271 			generateOutput();
272 		}
273 		catch (std::exception & e)
274 		{
275 			Log::log(Logger::ERROR, "error: %s\n", e.what());
276 			return 1;
277 		}
278 		catch (...)
279 		{
280 			Log::log(Logger::ERROR, "error: unexpected exception\n");
281 			return 1;
282 		}
283 
284 		return 0;
285 	}
286 
287 	/*!
288 	 * \brief Builds the output data blob, encrypts it, and writes it to the output file.
289 	 */
generateOutput()290 	void generateOutput()
291 	{
292 		// Create the output data blob and set it to the correct size.
293 		Blob data;
294 		data.setLength(OUTPUT_DATA_LENGTH);
295 
296 		// Fill it with random values.
297 		RandomNumberGenerator rng;
298 		rng.generateBlock(data.getData(), OUTPUT_DATA_LENGTH);
299 
300 		// Read the GPK and overlay it into the output data.
301 		// The first positional arg is the GPK file path.
302 		Blob gpk = readGPK(m_positionalArgs[0]);
303 		memcpy(data.getData() + OUTPUT_DATA_GPK_OFFSET, gpk.getData(), GPK_LENGTH);
304 
305 		// This is the key object for our crypto key.
306 		AESKey<128> cryptoKey = readKeyFile();
307 
308 		// Read the key file.
309 		// Encrypt the output data block.
310 		Rijndael cipher;
311 		cipher.init(Rijndael::CBC, Rijndael::Encrypt, cryptoKey, Rijndael::Key16Bytes, (uint8_t *)&kInitVector);
312 		cipher.blockEncrypt(data.getData(), OUTPUT_DATA_LENGTH * 8, data.getData());
313 
314 		// Open the output file.
315 		std::ofstream outputStream(m_outputPath.c_str(), std::ios_base::out | std::ios_base::trunc);
316 		if (!outputStream.is_open())
317 		{
318 			throw std::runtime_error(format_string("could not open output file %s", m_outputPath.c_str()));
319 		}
320 
321 		writeCArray(outputStream, data);
322 	}
323 
324 	/*!
325 	 * \brief Reads the group private key binary data.
326 	 */
readGPK(std::string & path)327 	Blob readGPK(std::string & path)
328 	{
329 		std::ifstream stream(path.c_str(), std::ios_base::in | std::ios_base::binary);
330 		if (!stream.is_open())
331 		{
332 			throw std::runtime_error("could not open group private key file");
333 		}
334 
335 		Blob gpk;
336 		gpk.setLength(GPK_LENGTH);
337 
338 		stream.read((char *)gpk.getData(), GPK_LENGTH);
339 
340 		return gpk;
341 	}
342 
343 	/*!
344 	 * \brief Returns a key object based on the user's specified key.
345 	 */
readKeyFile()346 	AESKey<128> readKeyFile()
347 	{
348 		if (m_keyFilePaths.size() > 0)
349 		{
350 			// Open the key file.
351 			std::string & keyPath = m_keyFilePaths[0];
352 			std::ifstream keyStream(keyPath.c_str(), std::ios_base::in);
353 			if (!keyStream.is_open())
354 			{
355 				throw std::runtime_error(format_string("unable to read key file %s\n", keyPath.c_str()));
356 			}
357 			keyStream.seekg(0);
358 
359 			// Read the first key in the file.
360 			AESKey<128> key(keyStream);
361 			return key;
362 		}
363 
364 		// Otherwise, create a zero key and return it.
365 		AESKey<128> defaultKey;
366 		return defaultKey;
367 
368 	}
369 
370 	/*!
371 	 * \brief Writes the given data blob as an array in a C source file.
372 	 */
writeCArray(std::ofstream & stream,const Blob & data)373 	void writeCArray(std::ofstream & stream, const Blob & data)
374 	{
375 		const uint8_t * dataPtr = data.getData();
376 		unsigned length = data.getLength();
377 
378 		// Write first line.
379 		std::string text = format_string("%s%sunsigned char %s[%d] = {", m_outputPrefix.c_str(), m_outputPrefix.size() > 0 ? " " : "", m_arrayName.c_str(), length);
380 		stream.write(text.c_str(), text.size());
381 
382 		// Write each word of the array.
383 		unsigned i = 0;
384 		while (i < length)
385 		{
386 			// Insert a comma at the end of the previous line unless this is the first word we're outputting.
387 			text = format_string("%s\n    0x%02x", i == 0 ? "" : ",", (*dataPtr++) & 0xff);
388 			stream.write(text.c_str(), text.size());
389 
390 			i++;
391 		}
392 
393 		// Write last line, terminating the array.
394 		text = "\n};\n\n";
395 		stream.write(text.c_str(), text.size());
396 	}
397 
398 	/*!
399 	 * \brief Turns on verbose logging.
400 	 */
setVerboseLogging()401 	void setVerboseLogging()
402 	{
403 		if (m_isVerbose)
404 		{
405 			// verbose only affects the INFO and DEBUG filter levels
406 			// if the user has selected quiet mode, it overrides verbose
407 			switch (Log::getLogger()->getFilterLevel())
408 			{
409 				case Logger::INFO:
410 					Log::getLogger()->setFilterLevel(Logger::INFO2);
411 					break;
412 				case Logger::DEBUG:
413 					Log::getLogger()->setFilterLevel(Logger::DEBUG2);
414 					break;
415 			}
416 		}
417 	}
418 
419 };
420 
421 /*!
422  * Main application entry point. Creates an sbtool instance and lets it take over.
423  */
main(int argc,char * argv[],char * envp[])424 int main(int argc, char* argv[], char* envp[])
425 {
426 	try
427 	{
428 		return encryptgpk(argc, argv).run();
429 	}
430 	catch (...)
431 	{
432 		Log::log(Logger::ERROR, "error: unexpected exception\n");
433 		return 1;
434 	}
435 
436 	return 0;
437 }
438 
439 
440 
441 
442 
443