1 /*
2  * File:  EncoreBootImageReader.cpp
3  *
4  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5  * See included license file for license details.
6  */
7 
8 #include "EncoreBootImageReader.h"
9 #include "SHA1.h"
10 #include "rijndael.h"
11 #include "RijndaelCBCMAC.h"
12 #include <assert.h>
13 #include "EndianUtilities.h"
14 #include "Logging.h"
15 
16 using namespace elftosb;
17 
18 //! \post Stream head points to just after the image header.
19 //! \exception read_error Thrown if the image header is invalid.
readImageHeader()20 void EncoreBootImageReader::readImageHeader()
21 {
22           // seek to beginning of the stream/file and read the plaintext header
23           m_stream.seekg(0, std::ios_base::beg);
24           if (m_stream.read((char *)&m_header, sizeof(m_header)).bad())
25           {
26                     throw read_error("failed to read image header");
27           }
28 
29           m_header.m_flags = ENDIAN_LITTLE_TO_HOST_U16(m_header.m_flags);
30           m_header.m_imageBlocks = ENDIAN_LITTLE_TO_HOST_U32(m_header.m_imageBlocks);
31           m_header.m_firstBootTagBlock = ENDIAN_LITTLE_TO_HOST_U32(m_header.m_firstBootTagBlock);
32           m_header.m_firstBootableSectionID = ENDIAN_LITTLE_TO_HOST_U32(m_header.m_firstBootableSectionID);
33           m_header.m_keyCount = ENDIAN_LITTLE_TO_HOST_U16(m_header.m_keyCount);
34           m_header.m_keyDictionaryBlock = ENDIAN_LITTLE_TO_HOST_U16(m_header.m_keyDictionaryBlock);
35           m_header.m_headerBlocks = ENDIAN_LITTLE_TO_HOST_U16(m_header.m_headerBlocks);
36           m_header.m_sectionCount = ENDIAN_LITTLE_TO_HOST_U16(m_header.m_sectionCount);
37           m_header.m_sectionHeaderSize = ENDIAN_LITTLE_TO_HOST_U16(m_header.m_sectionHeaderSize);
38           m_header.m_timestamp = ENDIAN_LITTLE_TO_HOST_U64(m_header.m_timestamp);
39 
40 //        m_header.m_componentVersion.m_major = ENDIAN_BIG_TO_HOST_U16(m_header.m_componentVersion.m_major);
41 //        m_header.m_componentVersion.m_minor = ENDIAN_BIG_TO_HOST_U16(m_header.m_componentVersion.m_minor);
42 //        m_header.m_componentVersion.m_revision = ENDIAN_BIG_TO_HOST_U16(m_header.m_componentVersion.m_revision);
43 
44 //        m_header.m_productVersion.m_major = ENDIAN_BIG_TO_HOST_U16(m_header.m_productVersion.m_major);
45 //        m_header.m_productVersion.m_minor = ENDIAN_BIG_TO_HOST_U16(m_header.m_productVersion.m_minor);
46 //        m_header.m_productVersion.m_revision = ENDIAN_BIG_TO_HOST_U16(m_header.m_productVersion.m_revision);
47 
48           // check header signature 1
49           if (m_header.m_signature[0] != 'S' || m_header.m_signature[1] != 'T' || m_header.m_signature[2] != 'M' || m_header.m_signature[3] != 'P')
50           {
51                     throw read_error("invalid signature 1");
52           }
53 
54           // check header signature 2 for version 1.1 and greater
55           if ((m_header.m_majorVersion > 1 || (m_header.m_majorVersion == 1 && m_header.m_minorVersion >= 1)) && (m_header.m_signature2[0] != 's' || m_header.m_signature2[1] != 'g' || m_header.m_signature2[2] != 't' || m_header.m_signature2[3] != 'l'))
56           {
57 //                  throw read_error("invalid signature 2");
58                     Log::log(Logger::WARNING, "warning: invalid signature 2\n");
59           }
60 }
61 
62 //! \pre The image header must have already been read with a call to readImageHeader().
63 //!
computeHeaderDigest(sha1_digest_t & digest)64 void EncoreBootImageReader::computeHeaderDigest(sha1_digest_t & digest)
65 {
66           CSHA1 hash;
67           hash.Reset();
68           hash.Update((uint8_t *)&m_header.m_signature, sizeof(m_header) - sizeof(sha1_digest_t));
69           hash.Final();
70           hash.GetHash(digest);
71 }
72 
73 //! \pre The image header must have already been read.
74 //! \pre The DEK must have been found already.
75 //! \post The stream head is at the end of the digest.
readImageDigest()76 void EncoreBootImageReader::readImageDigest()
77 {
78           unsigned digestPosition = sizeOfCipherBlocks(m_header.m_imageBlocks - 2);
79           m_stream.seekg(digestPosition, std::ios_base::beg);
80 
81           // read the two cipher blocks containing the digest, including padding
82           cipher_block_t digestBlocks[2];
83           if (m_stream.read((char *)&digestBlocks, sizeof(digestBlocks)).bad())
84           {
85                     throw read_error("failed to read image digest");
86           }
87 
88           // decrypt the digest
89           if (isEncrypted())
90           {
91                     Rijndael cipher;
92                     cipher.init(Rijndael::CBC, Rijndael::Decrypt, m_dek, Rijndael::Key16Bytes, m_header.m_iv);
93                     cipher.blockDecrypt((uint8_t *)&digestBlocks, sizeof(digestBlocks) * 8, (uint8_t *)&digestBlocks);
94           }
95 
96           // copy the digest out of the padded blocks
97           memcpy(m_digest, &digestBlocks, sizeof(m_digest));
98 }
99 
100 //! \pre The image header must have already been read with a call to readImageHeader().
101 //! \post The stream head is at the end of the image minus the last two cipher blocks.
102 //! \param digest Where to store the resulting digest.
103 //! \exception read_error Thrown if the image header is invalid.
computeImageDigest(sha1_digest_t & digest)104 void EncoreBootImageReader::computeImageDigest(sha1_digest_t & digest)
105 {
106           m_stream.seekg(0, std::ios_base::beg);
107 
108           CSHA1 hash;
109           hash.Reset();
110 
111           unsigned blockCount = m_header.m_imageBlocks - 2; // exclude digest at end of image
112           while (blockCount--)
113           {
114                     cipher_block_t block;
115                     if (m_stream.read((char *)&block, sizeof(block)).bad())
116                     {
117                               throw read_error("failed to read block while computing image digest");
118                     }
119                     hash.Update(block, sizeof(block));
120           }
121 
122           hash.Final();
123           hash.GetHash(digest);
124 }
125 
126 //! \pre Image header must have been read before this method is called.
127 //!
readSectionTable()128 void EncoreBootImageReader::readSectionTable()
129 {
130           // seek to the table
131           m_stream.seekg(sizeOfCipherBlocks(m_header.m_headerBlocks), std::ios_base::beg);
132 
133           unsigned sectionCount = m_header.m_sectionCount;
134           while (sectionCount--)
135           {
136                     EncoreBootImage::section_header_t header;
137                     if (m_stream.read((char *)&header, sizeof(header)).bad())
138                     {
139                               throw read_error("failed to read section header");
140                     }
141 
142                     // swizzle section header
143                     header.m_tag = ENDIAN_LITTLE_TO_HOST_U32(header.m_tag);
144                     header.m_offset = ENDIAN_LITTLE_TO_HOST_U32(header.m_offset);
145                     header.m_length = ENDIAN_LITTLE_TO_HOST_U32(header.m_length);
146                     header.m_flags = ENDIAN_LITTLE_TO_HOST_U32(header.m_flags);
147 
148                     m_sections.push_back(header);
149           }
150 }
151 
152 //! Requires that an OTP key has been provided as the sole argument. Passing the
153 //! key into this method lets the caller search the key dictionary for any number
154 //! of keys and determine which are valid. If \a kek is found in the dictionary,
155 //! the decrypted DEK is saved and true is returned. A result of false means
156 //! that \a kek was not found.
157 //!
158 //! \pre The image header and section table must have been read already.
159 //! \post The stream head points somewhere inside the key dictionary, or just after it.
160 //! \post If the search was successful, the #m_dek member will contain the decrypted
161 //!                 session key. Otherwise #m_dek is not modified.
162 //! \param kek Search for this KEK in the dictionary.
163 //! \retval true The DEK was found and decrypted. True is also returned when the
164 //!                 image is not encrypted at all.
165 //! \retval false No matching key entry was found. The image cannot be decrypted.
readKeyDictionary(const AESKey<128> & kek)166 bool EncoreBootImageReader::readKeyDictionary(const AESKey<128> & kek)
167 {
168           // do nothing if the image is not encrypted
169           if (!isEncrypted())
170           {
171                     return true;
172           }
173 
174           // first compute a CBC-MAC over the image header with our KEK
175           RijndaelCBCMAC mac(kek);
176           mac.update((const uint8_t *)&m_header, sizeof(m_header));
177 
178           // run the CBC-MAC over each entry in the section table too
179           section_array_t::iterator it = m_sections.begin();
180           for (; it != m_sections.end(); ++it)
181           {
182                     mac.update((const uint8_t *)&(*it), sizeof(EncoreBootImage::section_header_t));
183           }
184 
185           // get the CBC-MAC result
186           mac.finalize();
187           const RijndaelCBCMAC::block_t & macResult = mac.getMAC();
188 
189           // seek to the key dictionary
190           m_stream.seekg(sizeOfCipherBlocks(m_header.m_keyDictionaryBlock), std::ios_base::beg);
191 
192           // decipher each key entry
193           unsigned entries = m_header.m_keyCount;
194           while (entries--)
195           {
196                     // read the entry
197                     EncoreBootImage::dek_dictionary_entry_t entry;
198                     if (m_stream.read((char *)&entry, sizeof(entry)).bad())
199                     {
200                               throw read_error("failed to read key dictionary entry");
201                     }
202 
203                     // compare the CBC-MAC we computed with the one in this entry
204                     if (memcmp(macResult, entry.m_mac, sizeof(cipher_block_t)) == 0)
205                     {
206                               // it's a match! now decrypt this entry's key in place
207                               Rijndael cipher;
208                               cipher.init(Rijndael::CBC, Rijndael::Decrypt, kek, Rijndael::Key16Bytes, m_header.m_iv);
209                               cipher.blockDecrypt(entry.m_dek, sizeof(entry.m_dek) * 8, entry.m_dek);
210 
211                               m_dek = entry.m_dek;
212                               memset(entry.m_dek, 0, sizeof(entry.m_dek)); // wipe the key value from memory
213                               return true;
214                     }
215           }
216 
217           // if we exit the loop normally then no matching MAC was found
218           return false;
219 }
220 
221 //! Before the boot tag is added to the #m_bootTags member, some basic checks are performed.
222 //! The command tag field is checked to make sure it matches #ROM_TAG_CMD. And
223 //! the checksum field is verified to be sure it's correct.
224 //!
225 //! After the call to this method returns, the array of boot tags is accessible
226 //! with the getBootTags() method. The array is sorted in the order in which
227 //! the boot tags appeared in the image.
228 //!
229 //! \pre Image header must have been read.
230 //! \pre Key dictionary must have been read and a valid DEK found.
231 //! \post The stream head is left pointing just after the last boot tag.
232 //! \exception read_error A failure to read the boot tag, or a failure on one
233 //!                 of the consistency checks will cause this exception to be thrown.
readBootTags()234 void EncoreBootImageReader::readBootTags()
235 {
236           assert(m_header.m_firstBootTagBlock != 0);
237 
238           unsigned bootTagOffset = m_header.m_firstBootTagBlock;
239 
240           while (1)
241           {
242                     // seek to this boot tag and read it into a temporary buffer
243                     EncoreBootImage::boot_command_t header;
244                     m_stream.seekg(sizeOfCipherBlocks(bootTagOffset), std::ios_base::beg);
245                     if (m_stream.read((char *)&header, sizeof(header)).bad())
246                     {
247                               throw read_error("failed to read boot tag");
248                     }
249 
250                     // swizzle to command header
251                     header.m_flags = ENDIAN_LITTLE_TO_HOST_U16(header.m_flags);
252                     header.m_address = ENDIAN_LITTLE_TO_HOST_U32(header.m_address);
253                     header.m_count = ENDIAN_LITTLE_TO_HOST_U32(header.m_count);
254                     header.m_data = ENDIAN_LITTLE_TO_HOST_U32(header.m_data);
255 
256                     // decrypt in place
257                     if (isEncrypted())
258                     {
259                               Rijndael cipher;
260                               cipher.init(Rijndael::CBC, Rijndael::Decrypt, m_dek, Rijndael::Key16Bytes, m_header.m_iv);
261                               cipher.blockDecrypt((uint8_t *)&header, sizeof(header) * 8, (uint8_t *)&header);
262                     }
263 
264                     // perform some basic checks
265                     if (header.m_tag != EncoreBootImage::ROM_TAG_CMD)
266                     {
267                               throw read_error("boot tag is wrong command type");
268                     }
269 
270                     uint8_t checksum = calculateCommandChecksum(header);
271                     if (checksum != header.m_checksum)
272                     {
273                               throw read_error("boot tag checksum is invalid");
274                     }
275 
276                     // save this boot tag
277                     m_bootTags.push_back(header);
278 
279                     // and finally, update offset and break out of loop
280                     bootTagOffset += header.m_count + 1; // include this boot tag in offset
281                     if (header.m_flags & EncoreBootImage::ROM_LAST_TAG || bootTagOffset >= m_header.m_imageBlocks - 2)
282                     {
283                               break;
284                     }
285           }
286 }
287 
calculateCommandChecksum(EncoreBootImage::boot_command_t & header)288 uint8_t EncoreBootImageReader::calculateCommandChecksum(EncoreBootImage::boot_command_t & header)
289 {
290           uint8_t * bytes = reinterpret_cast<uint8_t *>(&header);
291           uint8_t checksum = 0x5a;
292           int i;
293 
294           // start at one to skip checksum field
295           for (i = 1; i < sizeof(header); ++i)
296           {
297                     checksum += bytes[i];
298           }
299 
300           return checksum;
301 }
302 
303 //! \param index The index of the section to read.
304 //!
305 //! \pre Both the image header and section table must have been read already before
306 //!                 calling this method.
307 //! \exception read_error This exception is raised if the stream reports an error while
308 //!                 trying to read from the section.
readSection(unsigned index)309 EncoreBootImage::Section * EncoreBootImageReader::readSection(unsigned index)
310 {
311           // look up section header
312           assert(index < m_sections.size());
313           EncoreBootImage::section_header_t & header = m_sections[index];
314 
315           // seek to the section
316           m_stream.seekg(sizeOfCipherBlocks(header.m_offset), std::ios_base::beg);
317 
318           uint8_t * contents = NULL;
319           try
320           {
321                     // allocate memory for the section contents and read the whole thing
322                     unsigned contentLength = sizeOfCipherBlocks(header.m_length);
323                     contents = new uint8_t[contentLength];
324                     if (m_stream.read((char *)contents, contentLength).bad())
325                     {
326                               throw read_error("failed to read section");
327                     }
328 
329                     // decrypt the entire section at once, if the image is encrypted and
330                     // the cleartext flag is not set
331                     if (isEncrypted() && (header.m_flags & EncoreBootImage::ROM_SECTION_CLEARTEXT) == 0)
332                     {
333                               Rijndael cipher;
334                               cipher.init(Rijndael::CBC, Rijndael::Decrypt, m_dek, Rijndael::Key16Bytes, m_header.m_iv);
335                               cipher.blockDecrypt(contents, contentLength * 8, contents);
336                     }
337 
338                     // create section object
339                     EncoreBootImage::Section * resultSection = NULL;
340                     if (header.m_flags & EncoreBootImage::ROM_SECTION_BOOTABLE)
341                     {
342                               // a boot command section.
343                               EncoreBootImage::BootSection * bootSection = new EncoreBootImage::BootSection(header.m_tag);
344 
345                               bootSection->fillFromData((cipher_block_t *)contents, header.m_length);
346 
347                               resultSection = bootSection;
348                     }
349                     else
350                     {
351                               // this is a raw data section
352                               EncoreBootImage::DataSection * dataSection = new EncoreBootImage::DataSection(header.m_tag);
353                               dataSection->setDataNoCopy(contents, contentLength);
354                               contents = NULL;
355                               resultSection = dataSection;
356                     }
357 
358                     return resultSection;
359           }
360           catch (...)
361           {
362                     if (contents)
363                     {
364                               delete [] contents;
365                     }
366                     throw;
367           }
368 }
369 
370 
371