1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2018 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
5
6# Support for a Chromium OS verified boot block, used to sign a read-write
7# section of the image.
8
9from collections import OrderedDict
10import os
11
12from binman.entry import EntryArg
13from binman.etype.collection import Entry_collection
14
15from dtoc import fdt_util
16from patman import tools
17
18class Entry_vblock(Entry_collection):
19    """An entry which contains a Chromium OS verified boot block
20
21    Properties / Entry arguments:
22        - content: List of phandles to entries to sign
23        - keydir: Directory containing the public keys to use
24        - keyblock: Name of the key file to use (inside keydir)
25        - signprivate: Name of provide key file to use (inside keydir)
26        - version: Version number of the vblock (typically 1)
27        - kernelkey: Name of the kernel key to use (inside keydir)
28        - preamble-flags: Value of the vboot preamble flags (typically 0)
29
30    Output files:
31        - input.<unique_name> - input file passed to futility
32        - vblock.<unique_name> - output file generated by futility (which is
33            used as the entry contents)
34
35    Chromium OS signs the read-write firmware and kernel, writing the signature
36    in this block. This allows U-Boot to verify that the next firmware stage
37    and kernel are genuine.
38    """
39    def __init__(self, section, etype, node):
40        super().__init__(section, etype, node)
41        (self.keydir, self.keyblock, self.signprivate, self.version,
42         self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([
43            EntryArg('keydir', str),
44            EntryArg('keyblock', str),
45            EntryArg('signprivate', str),
46            EntryArg('version', int),
47            EntryArg('kernelkey', str),
48            EntryArg('preamble-flags', int)])
49
50    def GetVblock(self, required):
51        """Get the contents of this entry
52
53        Args:
54            required: True if the data must be present, False if it is OK to
55                return None
56
57        Returns:
58            bytes content of the entry, which is the signed vblock for the
59                provided data
60        """
61        # Join up the data files to be signed
62        input_data = self.GetContents(required)
63        if input_data is None:
64            return None
65
66        uniq = self.GetUniqueName()
67        output_fname = tools.GetOutputFilename('vblock.%s' % uniq)
68        input_fname = tools.GetOutputFilename('input.%s' % uniq)
69        tools.WriteFile(input_fname, input_data)
70        prefix = self.keydir + '/'
71        args = [
72            'vbutil_firmware',
73            '--vblock', output_fname,
74            '--keyblock', prefix + self.keyblock,
75            '--signprivate', prefix + self.signprivate,
76            '--version', '%d' % self.version,
77            '--fv', input_fname,
78            '--kernelkey', prefix + self.kernelkey,
79            '--flags', '%d' % self.preamble_flags,
80        ]
81        #out.Notice("Sign '%s' into %s" % (', '.join(self.value), self.label))
82        stdout = tools.Run('futility', *args)
83        return tools.ReadFile(output_fname)
84
85    def ObtainContents(self):
86        data = self.GetVblock(False)
87        if data is None:
88            return False
89        self.SetContents(data)
90        return True
91
92    def ProcessContents(self):
93        # The blob may have changed due to WriteSymbols()
94        data = self.GetVblock(True)
95        return self.ProcessContentsUpdate(data)
96