Esempio n. 1
0
//! The boot tag for \a section is taken into account, thus making the
//! result offset point to the first block of the actual section data.
//!
//! \note The offset will only be valid if all encryption keys and all
//! sections have already been added to the image.
uint32_t EncoreBootImage::getSectionOffset(Section * section)
{
	// start with boot image headers 
	uint32_t offset = numberOfCipherBlocks(sizeof(boot_image_header_t));	// header
	offset += numberOfCipherBlocks(sizeof(section_header_t)) * sectionCount();	// section table
	offset += 2 * keyCount();	// key dictiontary
	
	// add up sections before this one
	section_iterator_t it = beginSection();
	for (; it != endSection() && *it != section; ++it)
	{
		Section * thisSection = *it;
		
		// insert padding for section alignment
		offset += getPadBlockCountForSection(thisSection, offset);
		
		// add one for boot tag associated with this section
		offset++;
		
		// now add the section's contents
		offset += thisSection->getBlockCount();
	}
	
	// and add padding for this section
	offset += getPadBlockCountForSection(section, offset);
	
	// skip over this section's boot tag
	offset++;
	
	return offset;
}
Esempio n. 2
0
//! \todo Optimize writing section data. Right now it only writes one block at a
//!		time, which is of course quite slow (in relative terms).
//!	\todo Refactor this into several different methods for writing each region
//!		of the image. Use a context structure to keep track of shared data between
//!		each of the methods.
//! \todo Refactor the section and boot tag writing code to only have a single
//!		copy of the block writing and encryption loop.
void EncoreBootImage::writeToStream(std::ostream & stream)
{
	// always generate the session key or DEK even if image is unencrypted
	m_sessionKey.randomize();
	
	// prepare to compute CBC-MACs with each KEK
	unsigned i;
	smart_array_ptr<RijndaelCBCMAC> macs(0);
	if (isEncrypted())
	{
		macs = new RijndaelCBCMAC[m_keys.size()];
		for (i=0; i < m_keys.size(); ++i)
		{
			RijndaelCBCMAC mac(m_keys[i]);
			(macs.get())[i] = mac;
		}
	}
	
	// prepare to compute SHA-1 digest over entire image
	CSHA1 hash;
	hash.Reset();
	
	// count of total blocks written to the file
	unsigned fileBlocksWritten = 0;

	// we need some pieces of the header down below
	boot_image_header_t imageHeader;
	prepareImageHeader(imageHeader);
	
	// write plaintext header
	{
		// write header
		assert(sizeOfPaddingForCipherBlocks(sizeof(boot_image_header_t)) == 0);
		stream.write(reinterpret_cast<char *>(&imageHeader), sizeof(imageHeader));
		fileBlocksWritten += numberOfCipherBlocks(sizeof(imageHeader));
		
		// update CBC-MAC over image header
		if (isEncrypted())
		{
			for (i=0; i < m_keys.size(); ++i)
			{
				(macs.get())[i].update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader));
			}
		}
		
		// update SHA-1
		hash.Update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader));
	}
	
	// write plaintext section table
	{
		section_iterator_t it = beginSection();
		for (; it != endSection(); ++it)
		{
			Section * section = *it;
			
			// write header for this section
			assert(sizeOfPaddingForCipherBlocks(sizeof(section_header_t)) == 0);
			section_header_t sectionHeader;
			section->fillSectionHeader(sectionHeader);
			stream.write(reinterpret_cast<char *>(&sectionHeader), sizeof(sectionHeader));
			fileBlocksWritten += numberOfCipherBlocks(sizeof(sectionHeader));
			
			// update CBC-MAC over this entry
			if (isEncrypted())
			{
				for (i=0; i < m_keys.size(); ++i)
				{
					(macs.get())[i].update(reinterpret_cast<uint8_t *>(&sectionHeader), sizeof(sectionHeader));
				}
			}
			
			// update SHA-1
			hash.Update(reinterpret_cast<uint8_t *>(&sectionHeader), sizeof(sectionHeader));
		}
	}
	
	// finished with the CBC-MAC
	if (isEncrypted())
	{
		for (i=0; i < m_keys.size(); ++i)
		{
			(macs.get())[i].finalize();
		}
	}
	
	// write key dictionary
	if (isEncrypted())
	{
		key_iterator_t it = beginKeys();
		for (i=0; it != endKeys(); ++it, ++i)
		{
			// write CBC-MAC result for this key, then update SHA-1
			RijndaelCBCMAC & mac = (macs.get())[i];
			const RijndaelCBCMAC::block_t & macResult = mac.getMAC();
			stream.write(reinterpret_cast<const char *>(&macResult), sizeof(RijndaelCBCMAC::block_t));
			hash.Update(reinterpret_cast<const uint8_t *>(&macResult), sizeof(RijndaelCBCMAC::block_t));
			fileBlocksWritten++;
			
			// encrypt DEK with this key, write it out, and update image digest
			Rijndael cipher;
			cipher.init(Rijndael::CBC, Rijndael::Encrypt, *it, Rijndael::Key16Bytes, imageHeader.m_iv);
			AESKey<128>::key_t wrappedSessionKey;
			cipher.blockEncrypt(m_sessionKey, sizeof(AESKey<128>::key_t) * 8, wrappedSessionKey);
			stream.write(reinterpret_cast<char *>(&wrappedSessionKey), sizeof(wrappedSessionKey));
			hash.Update(reinterpret_cast<uint8_t *>(&wrappedSessionKey), sizeof(wrappedSessionKey));
			fileBlocksWritten++;
		}
	}
	
	// write sections and boot tags
	{
		section_iterator_t it = beginSection();
		for (; it != endSection(); ++it)
		{
			section_iterator_t itCopy = it;
			bool isLastSection = (++itCopy == endSection());
			
			Section * section = *it;
			cipher_block_t block;
			unsigned blockCount = section->getBlockCount();
			unsigned blocksWritten = 0;
			
			Rijndael cipher;
			cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
			
			// Compute the number of padding blocks needed to align the section. This first
			// call to getPadBlockCountForOffset() passes an offset that excludes
			// the boot tag for this section.
			unsigned paddingBlocks = getPadBlockCountForSection(section, fileBlocksWritten);
			
			// Insert nop commands as padding to align the start of the section, if
			// the section has special alignment requirements.
			NopCommand nop;
			while (paddingBlocks--)
			{
				blockCount = nop.getBlockCount();
				blocksWritten = 0;
				while (blocksWritten < blockCount)
				{
					nop.getBlocks(blocksWritten, 1, &block);
					
					if (isEncrypted())
					{
						// re-init after encrypt to update IV
						cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
						cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
					}
					
					stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
					hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
					
					blocksWritten++;
					fileBlocksWritten++;
				}
			}
			
			// reinit cipher for boot tag
			cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
			
			// write boot tag
			TagCommand tag(*section);
			tag.setLast(isLastSection);
			if (!isLastSection)
			{
				// If this isn't the last section, the tag needs to include any
				// padding for the next section in its length, otherwise the ROM
				// won't be able to find the next section's boot tag.
				unsigned nextSectionOffset = fileBlocksWritten + section->getBlockCount() + 1;
				tag.setSectionLength(section->getBlockCount() + getPadBlockCountForSection(*itCopy, nextSectionOffset));
			}
			blockCount = tag.getBlockCount();
			blocksWritten = 0;
			while (blocksWritten < blockCount)
			{
				tag.getBlocks(blocksWritten, 1, &block);
				
				if (isEncrypted())
				{
					// re-init after encrypt to update IV
					cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
					cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
				}
				
				stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
				hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
				
				blocksWritten++;
				fileBlocksWritten++;
			}
			
			// reinit cipher for section data
			cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
			
			// write section data
			blockCount = section->getBlockCount();
			blocksWritten = 0;
			while (blocksWritten < blockCount)
			{
				section->getBlocks(blocksWritten, 1, &block);
				
				// Only encrypt the section contents if the entire boot image is encrypted
				// and the section doesn't have the "leave unencrypted" flag set. Even if the
				// section is unencrypted the boot tag will remain encrypted.
				if (isEncrypted() && !section->getLeaveUnencrypted())
				{
					// re-init after encrypt to update IV
					cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
					cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
				}
				
				stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
				hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
				
				blocksWritten++;
				fileBlocksWritten++;
			}
		}
	}
	
	// write SHA-1 digest over entire image
	{
		// allocate enough room for digest and bytes to pad out to the next cipher block
		const unsigned padBytes = sizeOfPaddingForCipherBlocks(sizeof(sha1_digest_t));
		unsigned digestBlocksSize = sizeof(sha1_digest_t) + padBytes;
		smart_array_ptr<uint8_t> digestBlocks = new uint8_t[digestBlocksSize];
		hash.Final();
		hash.GetHash(digestBlocks.get());
		
		// set the pad bytes to random values
		RandomNumberGenerator rng;
		rng.generateBlock(&(digestBlocks.get())[sizeof(sha1_digest_t)], padBytes);
		
		// encrypt with session key
		if (isEncrypted())
		{
			Rijndael cipher;
			cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
			cipher.blockEncrypt(digestBlocks.get(), digestBlocksSize * 8, digestBlocks.get());
		}
		
		// write to the stream
		stream.write(reinterpret_cast<char *>(digestBlocks.get()), digestBlocksSize);
	}
}
Esempio n. 3
0
//! The identifier, length, and flags fields are taken from \a section.
//!
//! \todo How does length get set correctly if the length is supposed to include
//!		this command?
EncoreBootImage::TagCommand::TagCommand(const Section & section)
{
	m_sectionIdentifier = section.getIdentifier();
	m_sectionLength = section.getBlockCount();
	m_sectionFlags = section.getFlags();
}