# 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 patman 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.GetOutputFilename('vblock.%s' % uniq)
        input_fname = tools.GetOutputFilename('input.%s' % uniq)
        tools.WriteFile(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,}',
            firmware=input_fname,
            kernelkey=prefix + self.kernelkey,
            flags=f'{self.preamble_flags}')
        if stdout is not None:
            data = tools.ReadFile(output_fname)
        else:
            # Bintool is missing; just use 4KB of zero data
            self.record_missing_bintool(self.futility)
            data = tools.GetBytes(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, tools):
        self.futility = self.AddBintool(tools, 'futility')
