| # SPDX-License-Identifier: GPL-2.0+ |
| # Copyright (c) 2018 Google, Inc |
| # Written by Simon Glass <sjg@chromium.org> |
| # |
| |
| # Support for a Chromium OS verified boot block, used to sign a read-write |
| # section of the image. |
| |
| from collections import OrderedDict |
| import os |
| |
| from binman.entry import EntryArg |
| from binman.etype.collection import Entry_collection |
| |
| from dtoc import fdt_util |
| from u_boot_pylib import tools |
| |
| class Entry_vblock(Entry_collection): |
| """An entry which contains a Chromium OS verified boot block |
| |
| Properties / Entry arguments: |
| - content: List of phandles to entries to sign |
| - keydir: Directory containing the public keys to use |
| - keyblock: Name of the key file to use (inside keydir) |
| - signprivate: Name of provide key file to use (inside keydir) |
| - version: Version number of the vblock (typically 1) |
| - kernelkey: Name of the kernel key to use (inside keydir) |
| - preamble-flags: Value of the vboot preamble flags (typically 0) |
| |
| Output files: |
| - input.<unique_name> - input file passed to futility |
| - vblock.<unique_name> - output file generated by futility (which is |
| used as the entry contents) |
| |
| Chromium OS signs the read-write firmware and kernel, writing the signature |
| in this block. This allows U-Boot to verify that the next firmware stage |
| and kernel are genuine. |
| """ |
| def __init__(self, section, etype, node): |
| super().__init__(section, etype, node) |
| self.futility = None |
| (self.keydir, self.keyblock, self.signprivate, self.version, |
| self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([ |
| EntryArg('keydir', str), |
| EntryArg('keyblock', str), |
| EntryArg('signprivate', str), |
| EntryArg('version', int), |
| EntryArg('kernelkey', str), |
| EntryArg('preamble-flags', int)]) |
| |
| def GetVblock(self, required): |
| """Get the contents of this entry |
| |
| Args: |
| required: True if the data must be present, False if it is OK to |
| return None |
| |
| Returns: |
| bytes content of the entry, which is the signed vblock for the |
| provided data |
| """ |
| # Join up the data files to be signed |
| input_data = self.GetContents(required) |
| if input_data is None: |
| return None |
| |
| uniq = self.GetUniqueName() |
| output_fname = tools.get_output_filename('vblock.%s' % uniq) |
| input_fname = tools.get_output_filename('input.%s' % uniq) |
| tools.write_file(input_fname, input_data) |
| prefix = self.keydir + '/' |
| stdout = self.futility.sign_firmware( |
| vblock=output_fname, |
| keyblock=prefix + self.keyblock, |
| signprivate=prefix + self.signprivate, |
| version=f'{self.version:d}', |
| firmware=input_fname, |
| kernelkey=prefix + self.kernelkey, |
| flags=f'{self.preamble_flags}') |
| if stdout is not None: |
| data = tools.read_file(output_fname) |
| else: |
| # Bintool is missing; just use 4KB of zero data |
| self.record_missing_bintool(self.futility) |
| data = tools.get_bytes(0, 4096) |
| return data |
| |
| def ObtainContents(self): |
| data = self.GetVblock(False) |
| if data is None: |
| return False |
| self.SetContents(data) |
| return True |
| |
| def ProcessContents(self): |
| # The blob may have changed due to WriteSymbols() |
| data = self.GetVblock(True) |
| return self.ProcessContentsUpdate(data) |
| |
| def AddBintools(self, btools): |
| super().AddBintools(btools) |
| self.futility = self.AddBintool(btools, 'futility') |