xref: /netbsd-src/external/bsd/elftosb/dist/sbtool/sbtool.cpp (revision 616728154accb2da0d5a0126e1280ca29f8ba569)
1993229b6Sjkunz /*
2993229b6Sjkunz  * File:	sbtool.cpp
3993229b6Sjkunz  *
4993229b6Sjkunz  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5993229b6Sjkunz  * See included license file for license details.
6993229b6Sjkunz  */
7993229b6Sjkunz 
8993229b6Sjkunz #include "stdafx.h"
9993229b6Sjkunz #include <iostream>
10993229b6Sjkunz #include <fstream>
11993229b6Sjkunz #include <sstream>
12993229b6Sjkunz #include <stdlib.h>
13993229b6Sjkunz #include <stdexcept>
14993229b6Sjkunz #include <stdio.h>
15993229b6Sjkunz #include "options.h"
16993229b6Sjkunz #include "EncoreBootImage.h"
17993229b6Sjkunz #include "smart_ptr.h"
18993229b6Sjkunz #include "Logging.h"
19993229b6Sjkunz #include "EncoreBootImageReader.h"
20993229b6Sjkunz #include "format_string.h"
21993229b6Sjkunz 
22993229b6Sjkunz using namespace elftosb;
23993229b6Sjkunz 
24993229b6Sjkunz //! The tool's name.
25993229b6Sjkunz const char k_toolName[] = "sbtool";
26993229b6Sjkunz 
27993229b6Sjkunz //! Current version number for the tool.
28993229b6Sjkunz const char k_version[] = "1.1.4";
29993229b6Sjkunz 
30993229b6Sjkunz //! Copyright string.
31993229b6Sjkunz const char k_copyright[] = "Copyright (c) 2006-2010 Freescale Semiconductor, Inc.\nAll rights reserved.";
32993229b6Sjkunz 
33993229b6Sjkunz //! Definition of command line options.
34993229b6Sjkunz static const char * k_optionsDefinition[] = {
35993229b6Sjkunz 	"?|help",
36993229b6Sjkunz 	"v|version",
37993229b6Sjkunz 	"k:key <file>",
38993229b6Sjkunz 	"z|zero-key",
39993229b6Sjkunz 	"x:extract",
40993229b6Sjkunz 	"b|binary",
41993229b6Sjkunz 	"d|debug",
42993229b6Sjkunz 	"q|quiet",
43993229b6Sjkunz 	"V|verbose",
44993229b6Sjkunz 	NULL
45993229b6Sjkunz };
46993229b6Sjkunz 
47993229b6Sjkunz //! Help string.
48993229b6Sjkunz const char k_usageText[] = "\nOptions:\n\
49993229b6Sjkunz   -?/--help                    Show this help\n\
50993229b6Sjkunz   -v/--version                 Display tool version\n\
51993229b6Sjkunz   -k/--key <file>              Add OTP key used for decryption\n\
52993229b6Sjkunz   -z/--zero-key                Add default key of all zeroes\n\
53993229b6Sjkunz   -x/--extract <index>         Extract section number <index>\n\
54993229b6Sjkunz   -b/--binary                  Extract section data as binary\n\
55993229b6Sjkunz   -d/--debug                   Enable debug output\n\
56993229b6Sjkunz   -q/--quiet                   Output only warnings and errors\n\
57993229b6Sjkunz   -V/--verbose                 Print extra detailed log information\n\n";
58993229b6Sjkunz 
59993229b6Sjkunz //! An array of strings.
60993229b6Sjkunz typedef std::vector<std::string> string_vector_t;
61993229b6Sjkunz 
62993229b6Sjkunz // prototypes
63993229b6Sjkunz int main(int argc, char* argv[], char* envp[]);
64993229b6Sjkunz 
65993229b6Sjkunz /*!
66993229b6Sjkunz  * \brief Class that encapsulates the sbtool interface.
67993229b6Sjkunz  *
68993229b6Sjkunz  * A single global logger instance is created during object construction. It is
69993229b6Sjkunz  * never freed because we need it up to the last possible minute, when an
70993229b6Sjkunz  * exception could be thrown.
71993229b6Sjkunz  */
72993229b6Sjkunz class sbtool
73993229b6Sjkunz {
74993229b6Sjkunz protected:
75993229b6Sjkunz 	int m_argc;							//!< Number of command line arguments.
76993229b6Sjkunz 	char ** m_argv;						//!< String value for each command line argument.
77993229b6Sjkunz 	StdoutLogger * m_logger;			//!< Singleton logger instance.
78993229b6Sjkunz 	string_vector_t m_keyFilePaths;		//!< Paths to OTP key files.
79993229b6Sjkunz 	string_vector_t m_positionalArgs;	//!< Arguments coming after explicit options.
80993229b6Sjkunz 	bool m_isVerbose;					//!< Whether the verbose flag was turned on.
81993229b6Sjkunz 	bool m_useDefaultKey;					//!< Include a default (zero) crypto key.
82993229b6Sjkunz 	bool m_doExtract;					//!< True if extract mode is on.
83993229b6Sjkunz 	unsigned m_sectionIndex;				//!< Index of section to extract.
84993229b6Sjkunz 	bool m_extractBinary;				//!< True if extraction output is binary, false for hex.
85993229b6Sjkunz 	smart_ptr<EncoreBootImageReader> m_reader;	//!< Boot image reader object.
86993229b6Sjkunz 
87993229b6Sjkunz public:
88993229b6Sjkunz 	/*!
89993229b6Sjkunz 	 * Constructor.
90993229b6Sjkunz 	 *
91993229b6Sjkunz 	 * Creates the singleton logger instance.
92993229b6Sjkunz 	 */
sbtool(int argc,char * argv[])93993229b6Sjkunz 	sbtool(int argc, char * argv[])
94993229b6Sjkunz 	:	m_argc(argc),
95993229b6Sjkunz 		m_argv(argv),
96993229b6Sjkunz 		m_logger(0),
97993229b6Sjkunz 		m_keyFilePaths(),
98993229b6Sjkunz 		m_positionalArgs(),
99993229b6Sjkunz 		m_isVerbose(false),
100993229b6Sjkunz 		m_useDefaultKey(false),
101993229b6Sjkunz 		m_doExtract(false),
102993229b6Sjkunz 		m_sectionIndex(0),
103993229b6Sjkunz 		m_extractBinary(false),
104993229b6Sjkunz 		m_reader()
105993229b6Sjkunz 	{
106993229b6Sjkunz 		// create logger instance
107993229b6Sjkunz 		m_logger = new StdoutLogger();
108993229b6Sjkunz 		m_logger->setFilterLevel(Logger::INFO);
109993229b6Sjkunz 		Log::setLogger(m_logger);
110993229b6Sjkunz 	}
111993229b6Sjkunz 
112993229b6Sjkunz 	/*!
113993229b6Sjkunz 	 * Destructor.
114993229b6Sjkunz 	 */
~sbtool()115993229b6Sjkunz 	~sbtool()
116993229b6Sjkunz 	{
117993229b6Sjkunz 	}
118993229b6Sjkunz 
119993229b6Sjkunz 	/*!
120993229b6Sjkunz 	 * Reads the command line options passed into the constructor.
121993229b6Sjkunz 	 *
122993229b6Sjkunz 	 * This method can return a return code to its caller, which will cause the
123993229b6Sjkunz 	 * tool to exit immediately with that return code value. Normally, though, it
124993229b6Sjkunz 	 * will return -1 to signal that the tool should continue to execute and
125993229b6Sjkunz 	 * all options were processed successfully.
126993229b6Sjkunz 	 *
127993229b6Sjkunz 	 * The Options class is used to parse command line options. See
128993229b6Sjkunz 	 * #k_optionsDefinition for the list of options and #k_usageText for the
129993229b6Sjkunz 	 * descriptive help for each option.
130993229b6Sjkunz 	 *
131993229b6Sjkunz 	 * \retval -1 The options were processed successfully. Let the tool run normally.
132993229b6Sjkunz 	 * \return A zero or positive result is a return code value that should be
133993229b6Sjkunz 	 *		returned from the tool as it exits immediately.
134993229b6Sjkunz 	 */
processOptions()135993229b6Sjkunz 	int processOptions()
136993229b6Sjkunz 	{
137993229b6Sjkunz 		Options options(*m_argv, k_optionsDefinition);
138993229b6Sjkunz 		OptArgvIter iter(--m_argc, ++m_argv);
139993229b6Sjkunz 
140993229b6Sjkunz 		// process command line options
141993229b6Sjkunz 		int optchar;
142993229b6Sjkunz 		const char * optarg;
143993229b6Sjkunz 		while (optchar = options(iter, optarg))
144993229b6Sjkunz 		{
145993229b6Sjkunz 			switch (optchar)
146993229b6Sjkunz 			{
147993229b6Sjkunz 				case '?':
148993229b6Sjkunz 					printUsage(options);
149993229b6Sjkunz 					return 0;
150993229b6Sjkunz 
151993229b6Sjkunz 				case 'v':
152993229b6Sjkunz 					printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
153993229b6Sjkunz 					return 0;
154993229b6Sjkunz 
155993229b6Sjkunz 				case 'k':
156993229b6Sjkunz 					m_keyFilePaths.push_back(optarg);
157993229b6Sjkunz 					break;
158993229b6Sjkunz 
159993229b6Sjkunz 				case 'z':
160993229b6Sjkunz 					m_useDefaultKey = true;
161993229b6Sjkunz 					break;
162993229b6Sjkunz 
163993229b6Sjkunz 				case 'x':
164993229b6Sjkunz 					m_doExtract = true;
165993229b6Sjkunz 					m_sectionIndex = strtoul(optarg, NULL, 0);
166993229b6Sjkunz 					break;
167993229b6Sjkunz 
168993229b6Sjkunz 				case 'b':
169993229b6Sjkunz 					m_extractBinary = true;
170993229b6Sjkunz 					Log::getLogger()->setFilterLevel(Logger::WARNING);
171993229b6Sjkunz 					break;
172993229b6Sjkunz 
173993229b6Sjkunz 				case 'd':
174993229b6Sjkunz 					Log::getLogger()->setFilterLevel(Logger::DEBUG);
175993229b6Sjkunz 					break;
176993229b6Sjkunz 
177993229b6Sjkunz 				case 'q':
178993229b6Sjkunz 					Log::getLogger()->setFilterLevel(Logger::WARNING);
179993229b6Sjkunz 					break;
180993229b6Sjkunz 
181993229b6Sjkunz 				case 'V':
182993229b6Sjkunz 					m_isVerbose = true;
183993229b6Sjkunz 					break;
184993229b6Sjkunz 
185993229b6Sjkunz 				default:
186993229b6Sjkunz 					Log::log(Logger::ERROR, "error: unrecognized option\n\n");
187993229b6Sjkunz 					printUsage(options);
188993229b6Sjkunz 					return 1;
189993229b6Sjkunz 			}
190993229b6Sjkunz 		}
191993229b6Sjkunz 
192993229b6Sjkunz 		// handle positional args
193993229b6Sjkunz 		if (iter.index() < m_argc)
194993229b6Sjkunz 		{
195993229b6Sjkunz //			Log::SetOutputLevel leveler(Logger::DEBUG);
196993229b6Sjkunz //			Log::log("positional args:\n");
197993229b6Sjkunz 			int i;
198993229b6Sjkunz 			for (i = iter.index(); i < m_argc; ++i)
199993229b6Sjkunz 			{
200993229b6Sjkunz //				Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
201993229b6Sjkunz 				m_positionalArgs.push_back(m_argv[i]);
202993229b6Sjkunz 			}
203993229b6Sjkunz 		}
204993229b6Sjkunz 
205993229b6Sjkunz 		// all is well
206993229b6Sjkunz 		return -1;
207993229b6Sjkunz 	}
208993229b6Sjkunz 
209993229b6Sjkunz 	/*!
210993229b6Sjkunz 	 * Prints help for the tool.
211993229b6Sjkunz 	 */
printUsage(Options & options)212993229b6Sjkunz 	void printUsage(Options & options)
213993229b6Sjkunz 	{
214993229b6Sjkunz 		options.usage(std::cout, "sb-file");
215*61672815Sjoerg 		printf("%s", k_usageText);
216993229b6Sjkunz 	}
217993229b6Sjkunz 
218993229b6Sjkunz 	/*!
219993229b6Sjkunz 	 * Core of the tool. Calls processOptions() to handle command line options
220993229b6Sjkunz 	 * before performing the real work the tool does.
221993229b6Sjkunz 	 */
run()222993229b6Sjkunz 	int run()
223993229b6Sjkunz 	{
224993229b6Sjkunz 		try
225993229b6Sjkunz 		{
226993229b6Sjkunz 			// read command line options
227993229b6Sjkunz 			int result;
228993229b6Sjkunz 			if ((result = processOptions()) != -1)
229993229b6Sjkunz 			{
230993229b6Sjkunz 				return result;
231993229b6Sjkunz 			}
232993229b6Sjkunz 
233993229b6Sjkunz 			// set verbose logging
234993229b6Sjkunz 			setVerboseLogging();
235993229b6Sjkunz 
236993229b6Sjkunz 			// make sure a file was provided
237993229b6Sjkunz 			if (m_positionalArgs.size() < 1)
238993229b6Sjkunz 			{
239993229b6Sjkunz 				throw std::runtime_error("no sb file path was provided");
240993229b6Sjkunz 			}
241993229b6Sjkunz 
242993229b6Sjkunz 			// read the boot image
243993229b6Sjkunz 			readBootImage();
244993229b6Sjkunz 		}
245993229b6Sjkunz 		catch (std::exception & e)
246993229b6Sjkunz 		{
247993229b6Sjkunz 			Log::log(Logger::ERROR, "error: %s\n", e.what());
248993229b6Sjkunz 			return 1;
249993229b6Sjkunz 		}
250993229b6Sjkunz 		catch (...)
251993229b6Sjkunz 		{
252993229b6Sjkunz 			Log::log(Logger::ERROR, "error: unexpected exception\n");
253993229b6Sjkunz 			return 1;
254993229b6Sjkunz 		}
255993229b6Sjkunz 
256993229b6Sjkunz 		return 0;
257993229b6Sjkunz 	}
258993229b6Sjkunz 
259993229b6Sjkunz 	/*!
260993229b6Sjkunz 	 * \brief Turns on verbose logging.
261993229b6Sjkunz 	 */
setVerboseLogging()262993229b6Sjkunz 	void setVerboseLogging()
263993229b6Sjkunz 	{
264993229b6Sjkunz 		if (m_isVerbose)
265993229b6Sjkunz 		{
266993229b6Sjkunz 			// verbose only affects the INFO and DEBUG filter levels
267993229b6Sjkunz 			// if the user has selected quiet mode, it overrides verbose
268993229b6Sjkunz 			switch (Log::getLogger()->getFilterLevel())
269993229b6Sjkunz 			{
270993229b6Sjkunz 				case Logger::INFO:
271993229b6Sjkunz 					Log::getLogger()->setFilterLevel(Logger::INFO2);
272993229b6Sjkunz 					break;
273993229b6Sjkunz 				case Logger::DEBUG:
274993229b6Sjkunz 					Log::getLogger()->setFilterLevel(Logger::DEBUG2);
275993229b6Sjkunz 					break;
276993229b6Sjkunz 			}
277993229b6Sjkunz 		}
278993229b6Sjkunz 	}
279993229b6Sjkunz 
280993229b6Sjkunz 	/*!
281993229b6Sjkunz 	 * \brief Opens and reads the boot image identified on the command line.
282993229b6Sjkunz 	 * \pre At least one position argument must be present.
283993229b6Sjkunz 	 */
readBootImage()284993229b6Sjkunz 	void readBootImage()
285993229b6Sjkunz 	{
286993229b6Sjkunz 		Log::SetOutputLevel infoLevel(Logger::INFO);
287993229b6Sjkunz 
288993229b6Sjkunz 		// open the sb file stream
289993229b6Sjkunz 		std::ifstream sbStream(m_positionalArgs[0].c_str(), std::ios_base::binary | std::ios_base::in);
290993229b6Sjkunz 		if (!sbStream.is_open())
291993229b6Sjkunz 		{
292993229b6Sjkunz 			throw std::runtime_error("failed to open input file");
293993229b6Sjkunz 		}
294993229b6Sjkunz 
295993229b6Sjkunz 		// create the boot image reader
296993229b6Sjkunz 		m_reader = new EncoreBootImageReader(sbStream);
297993229b6Sjkunz 
298993229b6Sjkunz 		// read image header
299993229b6Sjkunz 		m_reader->readImageHeader();
300993229b6Sjkunz 		const EncoreBootImage::boot_image_header_t & header = m_reader->getHeader();
301993229b6Sjkunz 		if (header.m_majorVersion > 1)
302993229b6Sjkunz 		{
303993229b6Sjkunz 			throw std::runtime_error(format_string("boot image format version is too new (format version %d.%d)\n", header.m_majorVersion, header.m_minorVersion));
304993229b6Sjkunz 		}
305993229b6Sjkunz 		Log::log("---- Boot image header ----\n");
306993229b6Sjkunz 		dumpImageHeader(header);
307993229b6Sjkunz 
308993229b6Sjkunz 		// compute SHA-1 over image header and test against the digest stored in the header
309993229b6Sjkunz 		sha1_digest_t computedDigest;
310993229b6Sjkunz 		m_reader->computeHeaderDigest(computedDigest);
311993229b6Sjkunz 		if (compareDigests(computedDigest, m_reader->getHeader().m_digest))
312993229b6Sjkunz 		{
313993229b6Sjkunz 			Log::log("Header digest is correct.\n");
314993229b6Sjkunz 		}
315993229b6Sjkunz 		else
316993229b6Sjkunz 		{
317993229b6Sjkunz 			Log::log(Logger::WARNING, "warning: stored SHA-1 header digest does not match the actual header digest\n");
318993229b6Sjkunz 			Log::log(Logger::WARNING, "\n---- Actual SHA-1 digest of image header ----\n");
319993229b6Sjkunz 			logHexArray(Logger::WARNING, (uint8_t *)&computedDigest, sizeof(computedDigest));
320993229b6Sjkunz 		}
321993229b6Sjkunz 
322993229b6Sjkunz 		// read the section table
323993229b6Sjkunz 		m_reader->readSectionTable();
324993229b6Sjkunz 		const EncoreBootImageReader::section_array_t & sectionTable = m_reader->getSections();
325993229b6Sjkunz 		EncoreBootImageReader::section_array_t::const_iterator it = sectionTable.begin();
326993229b6Sjkunz 		Log::log("\n---- Section table ----\n");
327993229b6Sjkunz 		unsigned n = 0;
328993229b6Sjkunz 		for (; it != sectionTable.end(); ++it, ++n)
329993229b6Sjkunz 		{
330993229b6Sjkunz 			const EncoreBootImage::section_header_t & sectionHeader = *it;
331993229b6Sjkunz 			Log::log("Section %d:\n", n);
332993229b6Sjkunz 			dumpSectionHeader(sectionHeader);
333993229b6Sjkunz 		}
334993229b6Sjkunz 
335993229b6Sjkunz 		// read the key dictionary
336993229b6Sjkunz 		// XXX need to support multiple keys, not just the first!
337993229b6Sjkunz 		if (m_reader->isEncrypted())
338993229b6Sjkunz 		{
339993229b6Sjkunz 			Log::log("\n---- Key dictionary ----\n");
340993229b6Sjkunz 			if (m_keyFilePaths.size() > 0 || m_useDefaultKey)
341993229b6Sjkunz 			{
342993229b6Sjkunz 				if (m_keyFilePaths.size() > 0)
343993229b6Sjkunz 				{
344993229b6Sjkunz 					std::string & keyPath = m_keyFilePaths[0];
345993229b6Sjkunz 					std::ifstream keyStream(keyPath.c_str(), std::ios_base::binary | std::ios_base::in);
346993229b6Sjkunz 					if (!keyStream.is_open())
347993229b6Sjkunz 					{
348993229b6Sjkunz 						Log::log(Logger::WARNING, "warning: unable to read key %s\n", keyPath.c_str());
349993229b6Sjkunz 					}
350993229b6Sjkunz 					AESKey<128> kek(keyStream);
351993229b6Sjkunz 
352993229b6Sjkunz 					// search for this key in the key dictionary
353993229b6Sjkunz 					if (!m_reader->readKeyDictionary(kek))
354993229b6Sjkunz 					{
355993229b6Sjkunz 						throw std::runtime_error("the provided key is not valid for this encrypted boot image");
356993229b6Sjkunz 					}
357993229b6Sjkunz 
358993229b6Sjkunz 					Log::log("\nKey %s was found in key dictionary.\n", keyPath.c_str());
359993229b6Sjkunz 				}
360993229b6Sjkunz 				else
361993229b6Sjkunz 				{
362993229b6Sjkunz 					// default key of zero, overriden if -k was used
363993229b6Sjkunz 					AESKey<128> defaultKek;
364993229b6Sjkunz 
365993229b6Sjkunz 					// search for this key in the key dictionary
366993229b6Sjkunz 					if (!m_reader->readKeyDictionary(defaultKek))
367993229b6Sjkunz 					{
368993229b6Sjkunz 						throw std::runtime_error("the default key is not valid for this encrypted boot image");
369993229b6Sjkunz 					}
370993229b6Sjkunz 
371993229b6Sjkunz 					Log::log("\nDefault key was found in key dictionary.\n");
372993229b6Sjkunz 				}
373993229b6Sjkunz 
374993229b6Sjkunz 				// print out the DEK
375993229b6Sjkunz 				AESKey<128> dek = m_reader->getKey();
376993229b6Sjkunz 				std::stringstream dekStringStream(std::ios_base::in | std::ios_base::out);
377993229b6Sjkunz 				dek.writeToStream(dekStringStream);
378993229b6Sjkunz 				std::string dekString = dekStringStream.str();
379993229b6Sjkunz // 				Log::log("\nData encryption key: %s\n", dekString.c_str());
380993229b6Sjkunz 				Log::log("\nData encryption key:\n");
381993229b6Sjkunz 				logHexArray(Logger::INFO, (const uint8_t *)&dek.getKey(), sizeof(AESKey<128>::key_t));
382993229b6Sjkunz 			}
383993229b6Sjkunz 			else
384993229b6Sjkunz 			{
385993229b6Sjkunz 				throw std::runtime_error("the image is encrypted but no key was provided");
386993229b6Sjkunz 			}
387993229b6Sjkunz 		}
388993229b6Sjkunz 
389993229b6Sjkunz 		// read the SHA-1 digest over the entire image. this is done after
390993229b6Sjkunz 		// reading the key dictionary because the digest is encrypted in
391993229b6Sjkunz 		// encrypted boot images.
392993229b6Sjkunz 		m_reader->readImageDigest();
393993229b6Sjkunz 		const sha1_digest_t & embeddedDigest = m_reader->getDigest();
394993229b6Sjkunz 		Log::log("\n---- SHA-1 digest of entire image ----\n");
395993229b6Sjkunz 		logHexArray(Logger::INFO, (const uint8_t *)&embeddedDigest, sizeof(embeddedDigest));
396993229b6Sjkunz 
397993229b6Sjkunz 		// compute the digest over the entire image and compare
398993229b6Sjkunz 		m_reader->computeImageDigest(computedDigest);
399993229b6Sjkunz 		if (compareDigests(computedDigest, embeddedDigest))
400993229b6Sjkunz 		{
401993229b6Sjkunz 			Log::log("Image digest is correct.\n");
402993229b6Sjkunz 		}
403993229b6Sjkunz 		else
404993229b6Sjkunz 		{
405993229b6Sjkunz 			Log::log(Logger::WARNING, "warning: stored SHA-1 digest does not match the actual digest\n");
406993229b6Sjkunz 			Log::log(Logger::WARNING, "\n---- Actual SHA-1 digest of entire image ----\n");
407993229b6Sjkunz 			logHexArray(Logger::WARNING, (uint8_t *)&computedDigest, sizeof(computedDigest));
408993229b6Sjkunz 		}
409993229b6Sjkunz 
410993229b6Sjkunz 		// read the boot tags
411993229b6Sjkunz 		m_reader->readBootTags();
412993229b6Sjkunz 		Log::log("\n---- Boot tags ----\n");
413993229b6Sjkunz 		unsigned block = header.m_firstBootTagBlock;
414993229b6Sjkunz 		const EncoreBootImageReader::boot_tag_array_t & tags = m_reader->getBootTags();
415993229b6Sjkunz 		EncoreBootImageReader::boot_tag_array_t::const_iterator tagIt = tags.begin();
416993229b6Sjkunz 		for (n = 0; tagIt != tags.end(); ++tagIt, ++n)
417993229b6Sjkunz 		{
418993229b6Sjkunz 			const EncoreBootImage::boot_command_t & command = *tagIt;
419993229b6Sjkunz 			Log::log("%04u: @ block %06u | id=0x%08x | length=%06u | flags=0x%08x\n", n, block, command.m_address, command.m_count, command.m_data);
420993229b6Sjkunz 
421993229b6Sjkunz 			if (command.m_data & EncoreBootImage::ROM_SECTION_BOOTABLE)
422993229b6Sjkunz 			{
423993229b6Sjkunz 				Log::log("        0x1 = ROM_SECTION_BOOTABLE\n");
424993229b6Sjkunz 			}
425993229b6Sjkunz 
426993229b6Sjkunz 			if (command.m_data & EncoreBootImage::ROM_SECTION_CLEARTEXT)
427993229b6Sjkunz 			{
428993229b6Sjkunz 				Log::log("        0x2 = ROM_SECTION_CLEARTEXT\n");
429993229b6Sjkunz 			}
430993229b6Sjkunz 
431993229b6Sjkunz 			block += command.m_count + 1;
432993229b6Sjkunz 		}
433993229b6Sjkunz 
434993229b6Sjkunz         // now read all of the sections
435993229b6Sjkunz 		Log::log(Logger::INFO2, "\n---- Sections ----\n");
436993229b6Sjkunz         for (n = 0; n < header.m_sectionCount; ++n)
437993229b6Sjkunz         {
438993229b6Sjkunz             EncoreBootImage::Section * section = m_reader->readSection(n);
439993229b6Sjkunz             section->debugPrint();
440993229b6Sjkunz 
441993229b6Sjkunz 			// Check if this is the section the user wants to extract.
442993229b6Sjkunz 			if (m_doExtract && n == m_sectionIndex)
443993229b6Sjkunz 			{
444993229b6Sjkunz 				extractSection(section);
445993229b6Sjkunz 			}
446993229b6Sjkunz         }
447993229b6Sjkunz 	}
448993229b6Sjkunz 
449993229b6Sjkunz 	//! \brief Dumps the contents of a section to stdout.
450993229b6Sjkunz 	//!
451993229b6Sjkunz 	//! If #m_extractBinary is true then the contents are written as
452993229b6Sjkunz 	//! raw binary to stdout. Otherwise the data is formatted using
453993229b6Sjkunz 	//! logHexArray().
extractSection(EncoreBootImage::Section * section)454993229b6Sjkunz 	void extractSection(EncoreBootImage::Section * section)
455993229b6Sjkunz 	{
456993229b6Sjkunz 		// Allocate buffer to hold section data.
457993229b6Sjkunz 		unsigned blockCount = section->getBlockCount();
458993229b6Sjkunz 		unsigned dataLength = sizeOfCipherBlocks(blockCount);
459993229b6Sjkunz 		smart_array_ptr<uint8_t> buffer = new uint8_t[dataLength];
460993229b6Sjkunz 		cipher_block_t * data = reinterpret_cast<cipher_block_t *>(buffer.get());
461993229b6Sjkunz 
462993229b6Sjkunz 		// Read section data into the buffer one block at a time.
463993229b6Sjkunz 		unsigned offset;
464993229b6Sjkunz 		for (offset = 0; offset < blockCount;)
465993229b6Sjkunz 		{
466993229b6Sjkunz 			unsigned blocksRead = section->getBlocks(offset, 1, data);
467993229b6Sjkunz 			offset += blocksRead;
468993229b6Sjkunz 			data += blocksRead;
469993229b6Sjkunz 		}
470993229b6Sjkunz 
471993229b6Sjkunz 		// Print header.
472993229b6Sjkunz 		Log::log(Logger::INFO, "\nSection %d contents:\n", m_sectionIndex);
473993229b6Sjkunz 
474993229b6Sjkunz 		// Now dump the extracted data to stdout.
475993229b6Sjkunz 		if (m_extractBinary)
476993229b6Sjkunz 		{
477993229b6Sjkunz 			if (fwrite(buffer.get(), 1, dataLength, stdout) != dataLength)
478993229b6Sjkunz 			{
479993229b6Sjkunz 				throw std::runtime_error(format_string("failed to write data to stdout (%d)", ferror(stdout)));
480993229b6Sjkunz 			}
481993229b6Sjkunz 		}
482993229b6Sjkunz 		else
483993229b6Sjkunz 		{
484993229b6Sjkunz 			// Use the warning log level so the data will be visible even in quiet mode.
485993229b6Sjkunz 			logHexArray(Logger::WARNING, buffer, dataLength);
486993229b6Sjkunz 		}
487993229b6Sjkunz 	}
488993229b6Sjkunz 
489993229b6Sjkunz 	//! \brief Compares two SHA-1 digests and returns whether they are equal.
490993229b6Sjkunz 	//! \retval true The two digests are equal.
491993229b6Sjkunz 	//! \retval false The \a a and \a b digests are different from each other.
compareDigests(const sha1_digest_t & a,const sha1_digest_t & b)492993229b6Sjkunz 	bool compareDigests(const sha1_digest_t & a, const sha1_digest_t & b)
493993229b6Sjkunz 	{
494993229b6Sjkunz 		return memcmp(a, b, sizeof(sha1_digest_t)) == 0;
495993229b6Sjkunz 	}
496993229b6Sjkunz 
497993229b6Sjkunz 	/*
498993229b6Sjkunz 	struct boot_image_header_t
499993229b6Sjkunz 	{
500993229b6Sjkunz 		union
501993229b6Sjkunz 		{
502993229b6Sjkunz 			sha1_digest_t m_digest;		//!< SHA-1 digest of image header. Also used as the crypto IV.
503993229b6Sjkunz 			struct
504993229b6Sjkunz 			{
505993229b6Sjkunz 				cipher_block_t m_iv;	//!< The first four bytes of the digest form the initialization vector.
506993229b6Sjkunz 				uint8_t m_extra[4];		//!< The leftover top four bytes of the SHA-1 digest.
507993229b6Sjkunz 			};
508993229b6Sjkunz 		};
509993229b6Sjkunz 		uint8_t m_signature[4];			//!< 'STMP', see #ROM_IMAGE_HEADER_SIGNATURE.
510993229b6Sjkunz 		uint16_t m_version;				//!< Version of the boot image format, see #ROM_BOOT_IMAGE_VERSION.
511993229b6Sjkunz 		uint16_t m_flags;				//!< Flags or options associated with the entire image.
512993229b6Sjkunz 		uint32_t m_imageBlocks;			//!< Size of entire image in blocks.
513993229b6Sjkunz 		uint32_t m_firstBootTagBlock;	//!< Offset from start of file to the first boot tag, in blocks.
514993229b6Sjkunz 		section_id_t m_firstBootableSectionID;	//!< ID of section to start booting from.
515993229b6Sjkunz 		uint16_t m_keyCount;			//!< Number of entries in DEK dictionary.
516993229b6Sjkunz 		uint16_t m_keyDictionaryBlock;	//!< Starting block number for the key dictionary.
517993229b6Sjkunz 		uint16_t m_headerBlocks;		//!< Size of this header, including this size word, in blocks.
518993229b6Sjkunz 		uint16_t m_sectionCount;		//!< Number of section headers in this table.
519993229b6Sjkunz 		uint16_t m_sectionHeaderSize;	//!< Size in blocks of a section header.
520993229b6Sjkunz 		uint8_t m_padding0[6];			//!< Padding to align #m_timestamp to long word.
521993229b6Sjkunz 		uint64_t m_timestamp;			//!< Timestamp when image was generated in microseconds since 1-1-2000.
522993229b6Sjkunz 		version_t m_productVersion;		//!< Product version.
523993229b6Sjkunz 		version_t m_componentVersion;	//!< Component version.
524993229b6Sjkunz 		uint16_t m_driveTag;
525993229b6Sjkunz 		uint8_t m_padding1[6];          //!< Padding to round up to next cipher block.
526993229b6Sjkunz 	};
527993229b6Sjkunz 	*/
dumpImageHeader(const EncoreBootImage::boot_image_header_t & header)528993229b6Sjkunz 	void dumpImageHeader(const EncoreBootImage::boot_image_header_t & header)
529993229b6Sjkunz 	{
530993229b6Sjkunz 		version_t vers;
531993229b6Sjkunz 
532993229b6Sjkunz 		Log::SetOutputLevel infoLevel(Logger::INFO);
533993229b6Sjkunz 		Log::log("Signature 1:           %c%c%c%c\n", header.m_signature[0], header.m_signature[1], header.m_signature[2], header.m_signature[3]);
534993229b6Sjkunz 		Log::log("Signature 2:           %c%c%c%c\n", header.m_signature2[0], header.m_signature2[1], header.m_signature2[2], header.m_signature2[3]);
535993229b6Sjkunz 		Log::log("Format version:        %d.%d\n", header.m_majorVersion, header.m_minorVersion);
536993229b6Sjkunz 		Log::log("Flags:                 0x%04x\n", header.m_flags);
537993229b6Sjkunz 		Log::log("Image blocks:          %u\n", header.m_imageBlocks);
538993229b6Sjkunz 		Log::log("First boot tag block:  %u\n", header.m_firstBootTagBlock);
539993229b6Sjkunz 		Log::log("First boot section ID: 0x%08x\n", header.m_firstBootableSectionID);
540993229b6Sjkunz 		Log::log("Key count:             %u\n", header.m_keyCount);
541993229b6Sjkunz 		Log::log("Key dictionary block:  %u\n", header.m_keyDictionaryBlock);
542993229b6Sjkunz 		Log::log("Header blocks:         %u\n", header.m_headerBlocks);
543993229b6Sjkunz 		Log::log("Section count:         %u\n", header.m_sectionCount);
544993229b6Sjkunz 		Log::log("Section header size:   %u\n", header.m_sectionHeaderSize);
545993229b6Sjkunz 		Log::log("Timestamp:             %llu\n", header.m_timestamp);
546993229b6Sjkunz 		vers = header.m_productVersion;
547993229b6Sjkunz 		vers.fixByteOrder();
548993229b6Sjkunz 		Log::log("Product version:       %x.%x.%x\n", vers.m_major, vers.m_minor, vers.m_revision);
549993229b6Sjkunz 		vers = header.m_componentVersion;
550993229b6Sjkunz 		vers.fixByteOrder();
551993229b6Sjkunz 		Log::log("Component version:     %x.%x.%x\n", vers.m_major, vers.m_minor, vers.m_revision);
552993229b6Sjkunz 		if (header.m_majorVersion == 1 && header.m_minorVersion >= 1)
553993229b6Sjkunz 		{
554993229b6Sjkunz 			Log::log("Drive tag:             0x%04x\n", header.m_driveTag);
555993229b6Sjkunz 		}
556993229b6Sjkunz 		Log::log("SHA-1 digest of header:\n");
557993229b6Sjkunz 		logHexArray(Logger::INFO, (uint8_t *)&header.m_digest, sizeof(header.m_digest));
558993229b6Sjkunz 	}
559993229b6Sjkunz 
dumpSectionHeader(const EncoreBootImage::section_header_t & header)560993229b6Sjkunz 	void dumpSectionHeader(const EncoreBootImage::section_header_t & header)
561993229b6Sjkunz 	{
562993229b6Sjkunz 		Log::SetOutputLevel infoLevel(Logger::INFO);
563993229b6Sjkunz 		Log::log("    Identifier: 0x%x\n", header.m_tag);
564993229b6Sjkunz 		Log::log("    Offset:     %d block%s (%d bytes)\n", header.m_offset, header.m_offset!=1?"s":"", sizeOfCipherBlocks(header.m_offset));
565993229b6Sjkunz 		Log::log("    Length:     %d block%s (%d bytes)\n", header.m_length, header.m_length!=1?"s":"", sizeOfCipherBlocks(header.m_length));
566993229b6Sjkunz 		Log::log("    Flags:      0x%08x\n", header.m_flags);
567993229b6Sjkunz 
568993229b6Sjkunz 		if (header.m_flags & EncoreBootImage::ROM_SECTION_BOOTABLE)
569993229b6Sjkunz 		{
570993229b6Sjkunz 			Log::log("                0x1 = ROM_SECTION_BOOTABLE\n");
571993229b6Sjkunz 		}
572993229b6Sjkunz 
573993229b6Sjkunz 		if (header.m_flags & EncoreBootImage::ROM_SECTION_CLEARTEXT)
574993229b6Sjkunz 		{
575993229b6Sjkunz 			Log::log("                0x2 = ROM_SECTION_CLEARTEXT\n");
576993229b6Sjkunz 		}
577993229b6Sjkunz 	}
578993229b6Sjkunz 
579993229b6Sjkunz 	/*!
580993229b6Sjkunz 	 * \brief Log an array of bytes as hex.
581993229b6Sjkunz 	 */
logHexArray(Logger::log_level_t level,const uint8_t * bytes,unsigned count)582993229b6Sjkunz 	void logHexArray(Logger::log_level_t level, const uint8_t * bytes, unsigned count)
583993229b6Sjkunz 	{
584993229b6Sjkunz 		Log::SetOutputLevel leveler(level);
585993229b6Sjkunz 
586993229b6Sjkunz 		unsigned i;
587993229b6Sjkunz 		for (i = 0; i < count; ++i, ++bytes)
588993229b6Sjkunz 		{
589993229b6Sjkunz 			if ((i % 16 == 0) && (i < count - 1))
590993229b6Sjkunz 			{
591993229b6Sjkunz 				if (i != 0)
592993229b6Sjkunz 				{
593993229b6Sjkunz 					Log::log("\n");
594993229b6Sjkunz 				}
595993229b6Sjkunz 				Log::log("    0x%08x: ", i);
596993229b6Sjkunz 			}
597993229b6Sjkunz 			Log::log("%02x ", *bytes & 0xff);
598993229b6Sjkunz 		}
599993229b6Sjkunz 
600993229b6Sjkunz 		Log::log("\n");
601993229b6Sjkunz 	}
602993229b6Sjkunz 
603993229b6Sjkunz };
604993229b6Sjkunz 
605993229b6Sjkunz /*!
606993229b6Sjkunz  * Main application entry point. Creates an sbtool instance and lets it take over.
607993229b6Sjkunz  */
main(int argc,char * argv[],char * envp[])608993229b6Sjkunz int main(int argc, char* argv[], char* envp[])
609993229b6Sjkunz {
610993229b6Sjkunz 	try
611993229b6Sjkunz 	{
612993229b6Sjkunz 		return sbtool(argc, argv).run();
613993229b6Sjkunz 	}
614993229b6Sjkunz 	catch (...)
615993229b6Sjkunz 	{
616993229b6Sjkunz 		Log::log(Logger::ERROR, "error: unexpected exception\n");
617993229b6Sjkunz 		return 1;
618993229b6Sjkunz 	}
619993229b6Sjkunz 
620993229b6Sjkunz 	return 0;
621993229b6Sjkunz }
622993229b6Sjkunz 
623993229b6Sjkunz 
624993229b6Sjkunz 
625993229b6Sjkunz 
626993229b6Sjkunz 
627