binman: implement signing FIT images during image build
The patch implement new property 'fit,sign' that can be declared at the top-level 'fit' node. If that option is declared, fit tryies to detect private keys directory among binman include directories. That directory than passed to mkimage using '-k' flag and that enable signing of FIT. Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org> Renumbered files, moved new tests to end: Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:

committed by
Simon Glass

parent
9e81f13dbb
commit
133c000ca3
@@ -22,7 +22,7 @@ class Bintoolmkimage(bintool.Bintool):
|
||||
|
||||
# pylint: disable=R0913
|
||||
def run(self, reset_timestamp=False, output_fname=None, external=False,
|
||||
pad=None, align=None):
|
||||
pad=None, align=None, priv_keys_dir=None):
|
||||
"""Run mkimage
|
||||
|
||||
Args:
|
||||
@@ -34,6 +34,7 @@ class Bintoolmkimage(bintool.Bintool):
|
||||
other things to be easily added later, if required, such as
|
||||
signatures
|
||||
align: Bytes to use for alignment of the FIT and its external data
|
||||
priv_keys_dir: Path to directory containing private keys
|
||||
version: True to get the mkimage version
|
||||
"""
|
||||
args = []
|
||||
@@ -45,6 +46,8 @@ class Bintoolmkimage(bintool.Bintool):
|
||||
args += ['-B', f'{align:x}']
|
||||
if reset_timestamp:
|
||||
args.append('-t')
|
||||
if priv_keys_dir:
|
||||
args += ['-k', f'{priv_keys_dir}']
|
||||
if output_fname:
|
||||
args += ['-F', output_fname]
|
||||
return self.run_cmd(*args)
|
||||
|
@@ -864,6 +864,13 @@ The top-level 'fit' node supports the following special properties:
|
||||
|
||||
fit,fdt-list-dir = "arch/arm/dts
|
||||
|
||||
fit,sign
|
||||
Enable signing FIT images via mkimage as described in
|
||||
verified-boot.rst. If the property is found, the private keys path is
|
||||
detected among binman include directories and passed to mkimage via
|
||||
-k flag. All the keys required for signing FIT must be available at
|
||||
time of signing and must be located in single include directory.
|
||||
|
||||
Substitutions
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import glob
|
||||
import os
|
||||
|
||||
import libfdt
|
||||
import os
|
||||
|
||||
from binman.entry import Entry, EntryArg
|
||||
from binman.etype.section import Entry_section
|
||||
@@ -101,6 +102,14 @@ class Entry_fit(Entry_section):
|
||||
In this case the input directories are ignored and all devicetree
|
||||
files must be in that directory.
|
||||
|
||||
fit,sign
|
||||
Enable signing FIT images via mkimage as described in
|
||||
verified-boot.rst. If the property is found, the private keys path
|
||||
is detected among binman include directories and passed to mkimage
|
||||
via -k flag. All the keys required for signing FIT must be
|
||||
available at time of signing and must be located in single include
|
||||
directory.
|
||||
|
||||
Substitutions
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -426,6 +435,7 @@ class Entry_fit(Entry_section):
|
||||
self._remove_props = props.split()
|
||||
self.mkimage = None
|
||||
self.fdtgrep = None
|
||||
self._fit_sign = None
|
||||
|
||||
def ReadNode(self):
|
||||
super().ReadNode()
|
||||
@@ -508,6 +518,45 @@ class Entry_fit(Entry_section):
|
||||
# are removed from self._entries later.
|
||||
self._priv_entries = dict(self._entries)
|
||||
|
||||
def _get_priv_keys_dir(self, data):
|
||||
"""Detect private keys path among binman include directories
|
||||
|
||||
Args:
|
||||
data: FIT image in binary format
|
||||
|
||||
Returns:
|
||||
str: Single path containing all private keys found or None
|
||||
|
||||
Raises:
|
||||
ValueError: Filename 'rsa2048.key' not found in input path
|
||||
ValueError: Multiple key paths found
|
||||
"""
|
||||
def _find_keys_dir(node):
|
||||
for subnode in node.subnodes:
|
||||
if subnode.name.startswith('signature'):
|
||||
if subnode.props.get('key-name-hint') is None:
|
||||
continue
|
||||
hint = subnode.props['key-name-hint'].value
|
||||
name = tools.get_input_filename(f"{hint}.key")
|
||||
path = os.path.dirname(name)
|
||||
if path not in paths:
|
||||
paths.append(path)
|
||||
else:
|
||||
_find_keys_dir(subnode)
|
||||
return None
|
||||
|
||||
fdt = Fdt.FromData(data)
|
||||
fdt.Scan()
|
||||
|
||||
paths = []
|
||||
|
||||
_find_keys_dir(fdt.GetRoot())
|
||||
|
||||
if len(paths) > 1:
|
||||
self.Raise("multiple key paths found (%s)" % ",".join(paths))
|
||||
|
||||
return paths[0] if len(paths) else None
|
||||
|
||||
def BuildSectionData(self, required):
|
||||
"""Build FIT entry contents
|
||||
|
||||
@@ -538,6 +587,8 @@ class Entry_fit(Entry_section):
|
||||
align = self._fit_props.get('fit,align')
|
||||
if align is not None:
|
||||
args.update({'align': fdt_util.fdt32_to_cpu(align.value)})
|
||||
if self._fit_props.get('fit,sign') is not None:
|
||||
args.update({'priv_keys_dir': self._get_priv_keys_dir(data)})
|
||||
if self.mkimage.run(reset_timestamp=True, output_fname=output_fname,
|
||||
**args) is None:
|
||||
if not self.GetAllowMissing():
|
||||
|
@@ -7804,6 +7804,101 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
|
||||
"""Test that binman can produce an iMX8 image"""
|
||||
self._DoTestFile('339_nxp_imx8.dts')
|
||||
|
||||
def testFitSignSimple(self):
|
||||
"""Test that image with FIT and signature nodes can be signed"""
|
||||
if not elf.ELF_TOOLS:
|
||||
self.skipTest('Python elftools not available')
|
||||
entry_args = {
|
||||
'of-list': 'test-fdt1',
|
||||
'default-dt': 'test-fdt1',
|
||||
'atf-bl31-path': 'bl31.elf',
|
||||
}
|
||||
data = tools.read_file(self.TestFile("340_rsa2048.key"))
|
||||
self._MakeInputFile("keys/rsa2048.key", data)
|
||||
|
||||
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
|
||||
keys_subdir = os.path.join(self._indir, "keys")
|
||||
data = self._DoReadFileDtb(
|
||||
'340_fit_signature.dts',
|
||||
entry_args=entry_args,
|
||||
extra_indirs=[test_subdir, keys_subdir])[0]
|
||||
|
||||
dtb = fdt.Fdt.FromData(data)
|
||||
dtb.Scan()
|
||||
|
||||
conf = dtb.GetNode('/configurations/conf-uboot-1')
|
||||
self.assertIsNotNone(conf)
|
||||
signature = conf.FindNode('signature')
|
||||
self.assertIsNotNone(signature)
|
||||
self.assertIsNotNone(signature.props.get('value'))
|
||||
|
||||
images = dtb.GetNode('/images')
|
||||
self.assertIsNotNone(images)
|
||||
for subnode in images.subnodes:
|
||||
signature = subnode.FindNode('signature')
|
||||
self.assertIsNotNone(signature)
|
||||
self.assertIsNotNone(signature.props.get('value'))
|
||||
|
||||
def testFitSignKeyNotFound(self):
|
||||
"""Test that missing keys raise an error"""
|
||||
if not elf.ELF_TOOLS:
|
||||
self.skipTest('Python elftools not available')
|
||||
entry_args = {
|
||||
'of-list': 'test-fdt1',
|
||||
'default-dt': 'test-fdt1',
|
||||
'atf-bl31-path': 'bl31.elf',
|
||||
}
|
||||
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self._DoReadFileDtb(
|
||||
'340_fit_signature.dts',
|
||||
entry_args=entry_args,
|
||||
extra_indirs=[test_subdir])[0]
|
||||
self.assertIn(
|
||||
'Filename \'rsa2048.key\' not found in input path',
|
||||
str(e.exception))
|
||||
|
||||
def testFitSignMultipleKeyPaths(self):
|
||||
"""Test that keys found in multiple paths raise an error"""
|
||||
if not elf.ELF_TOOLS:
|
||||
self.skipTest('Python elftools not available')
|
||||
entry_args = {
|
||||
'of-list': 'test-fdt1',
|
||||
'default-dt': 'test-fdt1',
|
||||
'atf-bl31-path': 'bl31.elf',
|
||||
}
|
||||
data = tools.read_file(self.TestFile("340_rsa2048.key"))
|
||||
self._MakeInputFile("keys1/rsa2048.key", data)
|
||||
data = tools.read_file(self.TestFile("340_rsa2048.key"))
|
||||
self._MakeInputFile("keys2/conf-rsa2048.key", data)
|
||||
|
||||
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
|
||||
keys_subdir1 = os.path.join(self._indir, "keys1")
|
||||
keys_subdir2 = os.path.join(self._indir, "keys2")
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self._DoReadFileDtb(
|
||||
'341_fit_signature.dts',
|
||||
entry_args=entry_args,
|
||||
extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
|
||||
self.assertIn(
|
||||
'Node \'/binman/fit\': multiple key paths found',
|
||||
str(e.exception))
|
||||
|
||||
def testFitSignNoSingatureNodes(self):
|
||||
"""Test that fit,sign doens't raise error if no signature nodes found"""
|
||||
if not elf.ELF_TOOLS:
|
||||
self.skipTest('Python elftools not available')
|
||||
entry_args = {
|
||||
'of-list': 'test-fdt1',
|
||||
'default-dt': 'test-fdt1',
|
||||
'atf-bl31-path': 'bl31.elf',
|
||||
}
|
||||
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
|
||||
self._DoReadFileDtb(
|
||||
'342_fit_signature.dts',
|
||||
entry_args=entry_args,
|
||||
extra_indirs=[test_subdir])[0]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
98
tools/binman/test/340_fit_signature.dts
Normal file
98
tools/binman/test/340_fit_signature.dts
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
fit {
|
||||
description = "test desc";
|
||||
#address-cells = <1>;
|
||||
fit,fdt-list = "of-list";
|
||||
fit,sign;
|
||||
|
||||
images {
|
||||
u-boot {
|
||||
description = "test u-boot";
|
||||
type = "standalone";
|
||||
arch = "arm64";
|
||||
os = "u-boot";
|
||||
compression = "none";
|
||||
load = <0x00000000>;
|
||||
entry = <0x00000000>;
|
||||
|
||||
u-boot-nodtb {
|
||||
};
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
};
|
||||
};
|
||||
@atf-SEQ {
|
||||
fit,operation = "split-elf";
|
||||
description = "test tf-a";
|
||||
type = "firmware";
|
||||
arch = "arm64";
|
||||
os = "arm-trusted-firmware";
|
||||
compression = "none";
|
||||
fit,load;
|
||||
fit,entry;
|
||||
fit,data;
|
||||
|
||||
atf-bl31 {
|
||||
};
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
};
|
||||
};
|
||||
@fdt-SEQ {
|
||||
description = "test fdt";
|
||||
type = "flat_dt";
|
||||
compression = "none";
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configurations {
|
||||
default = "@conf-uboot-DEFAULT-SEQ";
|
||||
@conf-uboot-SEQ {
|
||||
description = "uboot config";
|
||||
fdt = "fdt-SEQ";
|
||||
fit,firmware = "u-boot";
|
||||
fit,loadables;
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
sign-images = "firmware", "loadables", "fdt";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
28
tools/binman/test/340_rsa2048.key
Normal file
28
tools/binman/test/340_rsa2048.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDVUiT2JAF8Ajcx
|
||||
3XTB5qdGxuPMVFcXKJH+4L66oSt4YUBGi1bClo80U2azu08BTzk2Jzv6hez/mvzL
|
||||
hBvL3WnPwMl5vdOxb1kvUQyKLSw2bkM8VB0X1jGsKsKjzArg/aI8RknfiaSc5jua
|
||||
2lqwUFwv2RMF8jvIMN/1GnTLdECeMFVgVFSFkzIocISAHGPoGUOxTf8xK7o0x4RX
|
||||
NzB+95RtIqTQ5Az/KPVCOcQR5ETrUBXHF1I0rYjJjHHO4dUxxfDqFabt60EzQ/R2
|
||||
oZu58C4y0TrRI98g4hVPBYapildWjaNQm1Exa4ZaSDVl01OXsFW9Dm80PqfW4tTH
|
||||
Cm4nuCq5AgMBAAECggEBAIoG5b2SHJfFwzrzpQmVmeTU6i6a3+MvMBAwEZkmkb8J
|
||||
hhJfNFsiGjTsRgbDiuI5BbbBejCmmWvmN+3jZCzr7fwsLPEl36TufFF+atO5WOM7
|
||||
Qyv07QIwaOGSpXBgpSVhV6kSfdgy8p1G54hSAt4UkSGwnnt5ei8VWMP6Q1oltW3k
|
||||
f9DQ/ar4UEVa4jlJU3xqchcUTiKBKSH6pMC/Fqlq8x5JTLmk1Yb6C2UNcgJYez1u
|
||||
sHkdCA0FG3rFPrpFoQ1LUjMj1uEYNAxM3jOxE7Uvmk4yo9WpQDY7cRb2+Th9YY8a
|
||||
IKQ2s81Yg2TmkGzr8f5nrZz3WbAmQhQgsKbwlo6snjUCgYEA7kBOt0JlU7bJTfOr
|
||||
9s51g2VUfIH9lDS2Eh8MY+Bt6Y0Kdw/UK4HR8ZlN/nn0bHuHkc12K8lXEsQpgIEW
|
||||
DaqHytZJHqFs2egzKu/IvQYZ2WXEMj47LZQxEDHO9gtjE+5qCW9yJGqxW9BJKPVD
|
||||
F4spus4NqC+yD5OHM+6ESUtL/wMCgYEA5TZj6OHmECeh3efrwHqjDcjrqQbOTozU
|
||||
KPCNCY3Pv4Cg4xas/L93TE2CY6HJTr6mwEMUM+M4Ujjj15VCmSDQ/YYcGau1jo+f
|
||||
XdphOEENrPwoe9ATWIyBpT/wDrEz3L6JbE9dWMYY8vKYESt3qhVqDlbpmnYl8Jm+
|
||||
O3r5Cy2NlJMCgYEAyqzsCZuy5QcesnByvm8dqpxdxdkzJYu9wyakfKZj+gUgfO57
|
||||
OFOkjFk07yFB27MuPctCFredmfpDr+ygHRoPkG7AHw2Fss2EEaeP5bU18ilPQMqN
|
||||
vxVMs5EblVVUgJUVoVcsC2yz2f4S7oPOAk5BPoehOIzydauznWrvIAas7I8CgYBr
|
||||
CFHxLoNq6cbZQ3JACERZrIf2/vmZjoOHtoR1gKYRK7R1NmKDB7lihRMtCSBix/4/
|
||||
61Lkw+bJ5kzmn4lgzgUpTdWTWy5FquVlQxOA3EfRjlItNsXB5KKpksi7Y53vJ34u
|
||||
eIUDbkW6NPQzmFOhtaw3k/gzq5Yd2v0M82iWAqiJRwKBgQCl2+e2cjISK31QhKTC
|
||||
puhwQ0/YuC3zlwMXQgB3nPw8b9RlaDTMrRBCIUFIrrX11tHswGWpyVsxW2AvZ3Zm
|
||||
jsWpwGkUdpRdXJBhSaisV/PA+x3kYhpibzEI8FrzhU69zNROCb8CTkN4WcdBdq6J
|
||||
PUh/jRtKoE79qrlnIlNvFoz2gQ==
|
||||
-----END PRIVATE KEY-----
|
98
tools/binman/test/341_fit_signature.dts
Normal file
98
tools/binman/test/341_fit_signature.dts
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
fit {
|
||||
description = "test desc";
|
||||
#address-cells = <1>;
|
||||
fit,fdt-list = "of-list";
|
||||
fit,sign;
|
||||
|
||||
images {
|
||||
u-boot {
|
||||
description = "test u-boot";
|
||||
type = "standalone";
|
||||
arch = "arm64";
|
||||
os = "u-boot";
|
||||
compression = "none";
|
||||
load = <0x00000000>;
|
||||
entry = <0x00000000>;
|
||||
|
||||
u-boot-nodtb {
|
||||
};
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
};
|
||||
};
|
||||
@atf-SEQ {
|
||||
fit,operation = "split-elf";
|
||||
description = "test tf-a";
|
||||
type = "firmware";
|
||||
arch = "arm64";
|
||||
os = "arm-trusted-firmware";
|
||||
compression = "none";
|
||||
fit,load;
|
||||
fit,entry;
|
||||
fit,data;
|
||||
|
||||
atf-bl31 {
|
||||
};
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
};
|
||||
};
|
||||
@fdt-SEQ {
|
||||
description = "test fdt";
|
||||
type = "flat_dt";
|
||||
compression = "none";
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "rsa2048";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configurations {
|
||||
default = "@conf-uboot-DEFAULT-SEQ";
|
||||
@conf-uboot-SEQ {
|
||||
description = "uboot config";
|
||||
fdt = "fdt-SEQ";
|
||||
fit,firmware = "u-boot";
|
||||
fit,loadables;
|
||||
|
||||
hash {
|
||||
algo = "sha256";
|
||||
};
|
||||
|
||||
signature {
|
||||
algo = "sha256,rsa2048";
|
||||
key-name-hint = "conf-rsa2048";
|
||||
sign-images = "firmware", "loadables", "fdt";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
61
tools/binman/test/342_fit_signature.dts
Normal file
61
tools/binman/test/342_fit_signature.dts
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
fit {
|
||||
description = "test desc";
|
||||
#address-cells = <1>;
|
||||
fit,fdt-list = "of-list";
|
||||
fit,sign;
|
||||
|
||||
images {
|
||||
u-boot {
|
||||
description = "test u-boot";
|
||||
type = "standalone";
|
||||
arch = "arm64";
|
||||
os = "u-boot";
|
||||
compression = "none";
|
||||
load = <0x00000000>;
|
||||
entry = <0x00000000>;
|
||||
|
||||
u-boot-nodtb {
|
||||
};
|
||||
};
|
||||
@atf-SEQ {
|
||||
fit,operation = "split-elf";
|
||||
description = "test tf-a";
|
||||
type = "firmware";
|
||||
arch = "arm64";
|
||||
os = "arm-trusted-firmware";
|
||||
compression = "none";
|
||||
fit,load;
|
||||
fit,entry;
|
||||
fit,data;
|
||||
|
||||
atf-bl31 {
|
||||
};
|
||||
};
|
||||
@fdt-SEQ {
|
||||
description = "test fdt";
|
||||
type = "flat_dt";
|
||||
compression = "none";
|
||||
};
|
||||
};
|
||||
|
||||
configurations {
|
||||
default = "@conf-uboot-DEFAULT-SEQ";
|
||||
@conf-uboot-SEQ {
|
||||
description = "uboot config";
|
||||
fdt = "fdt-SEQ";
|
||||
fit,firmware = "u-boot";
|
||||
fit,loadables;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
Reference in New Issue
Block a user