summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Luebbe <jlu@pengutronix.de>2024-04-23 12:21:02 +0200
committerKhem Raj <raj.khem@gmail.com>2024-04-23 15:18:30 -0700
commit6f4501734f28f3133ad9ac70b8d320cca8b40c4e (patch)
tree8895b7046bb8f566ae3f55f1bf175cad173ea29a
parent94a65e3ff65f69bad8f34690ce1b36cb97b039c0 (diff)
downloadmeta-openembedded-6f4501734f28f3133ad9ac70b8d320cca8b40c4e.tar.gz
Add class for appending dm-verity hash data to block device images
Add support to generate a dm-verity image and the parameters required to assemble the corresponding table for the device-mapper driver. The latter will be stored in the file ${DEPLOY_DIR_IMAGE}/<IMAGE_LINK_NAME>.verity-params. Note that in the resulting image the hash tree data is appended to the contents of the original image without an explicit superblock to keep things simple and compact. The above mentioned parameter file can be sourced by a shell to finally create the desired blockdevice via "dmsetup" (found in meta-oe's recipe "libdevmapper"), e.g. . <IMAGE_LINK_NAME>.verity-params dmsetup create <dm_dev_name> --readonly --table "0 $VERITY_DATA_SECTORS verity \ 1 <dev> <hash_dev> \ $VERITY_DATA_BLOCK_SIZE $VERITY_HASH_BLOCK_SIZE \ $VERITY_DATA_BLOCKS $VERITY_DATA_BLOCKS \ $VERITY_HASH_ALGORITHM $VERITY_ROOT_HASH $VERITY_SALT \ 1 ignore_zero_blocks" As the hash tree data is found at the end of the image, <dev> and <hash_dev> should be the same blockdevice in the command shown above while <dm_dev_name> is the name of the to be created dm-verity-device. The root hash is calculated using a salt to make attacks more difficult. Thus, please grant each image recipe its own salt which could be generated e.g. via dd if=/dev/random bs=1k count=1 | sha256sum and assign it to the parameter VERITY_SALT. Signed-off-by: Jan Luebbe <jlu@pengutronix.de> Signed-off-by: Rouven Czerwinski <r.czerwinski@pengutronix.de> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de> Signed-off-by: Ulrich Ölmann <u.oelmann@pengutronix.de> Signed-off-by: Khem Raj <raj.khem@gmail.com>
-rw-r--r--meta-oe/classes/image_types_verity.bbclass137
1 files changed, 137 insertions, 0 deletions
diff --git a/meta-oe/classes/image_types_verity.bbclass b/meta-oe/classes/image_types_verity.bbclass
new file mode 100644
index 000000000..b42217c45
--- /dev/null
+++ b/meta-oe/classes/image_types_verity.bbclass
@@ -0,0 +1,137 @@
1# SPDX-License-Identifier: MIT
2#
3# Copyright Pengutronix <yocto@pengutronix.de>
4#
5
6# Support generating a dm-verity image and the parameters required to assemble
7# the corresponding table for the device-mapper driver. The latter will be
8# stored in the file ${DEPLOY_DIR_IMAGE}/<IMAGE_LINK_NAME>.verity-params. Note
9# that in the resulting image the hash tree data is appended to the contents of
10# the original image without an explicit superblock to keep things simple and
11# compact.
12#
13# The above mentioned parameter file can be sourced by a shell to finally create
14# the desired blockdevice via "dmsetup" (found in meta-oe's recipe
15# "libdevmapper"), e.g.
16#
17# . <IMAGE_LINK_NAME>.verity-params
18# dmsetup create <dm_dev_name> --readonly --table "0 $VERITY_DATA_SECTORS \
19# verity 1 <dev> <hash_dev> \
20# $VERITY_DATA_BLOCK_SIZE $VERITY_HASH_BLOCK_SIZE \
21# $VERITY_DATA_BLOCKS $VERITY_DATA_BLOCKS \
22# $VERITY_HASH_ALGORITHM $VERITY_ROOT_HASH $VERITY_SALT \
23# 1 ignore_zero_blocks"
24#
25# As the hash tree data is found at the end of the image, <dev> and <hash_dev>
26# should be the same blockdevice in the command shown above while <dm_dev_name>
27# is the name of the to be created dm-verity-device.
28#
29# The root hash is calculated using a salt to make attacks more difficult. Thus,
30# please grant each image recipe its own salt which could be generated e.g. via
31#
32# dd if=/dev/random bs=1k count=1 | sha256sum
33#
34# and assign it to the parameter VERITY_SALT.
35
36inherit image-artifact-names
37
38do_image_verity[depends] += "cryptsetup-native:do_populate_sysroot"
39
40CLASS_VERITY_SALT = "4e5f0d9b6ccac5e843598d4e4545046232b48451a399acb2106822b43679b375"
41VERITY_SALT ?= "${CLASS_VERITY_SALT}"
42VERITY_BLOCK_SIZE ?= "4096"
43VERITY_IMAGE_FSTYPE ?= "ext4"
44VERITY_IMAGE_SUFFIX ?= ".verity"
45VERITY_INPUT_IMAGE ?= "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.${VERITY_IMAGE_FSTYPE}"
46
47IMAGE_TYPEDEP:verity = "${VERITY_IMAGE_FSTYPE}"
48IMAGE_TYPES_MASKED += "verity"
49
50python __anonymous() {
51 if 'verity' not in d.getVar('IMAGE_FSTYPES'):
52 return
53
54 dep_task = 'do_image_{}'.format(d.getVar('VERITY_IMAGE_FSTYPE').replace('-', '_'))
55 bb.build.addtask('do_image_verity', 'do_image_complete', dep_task, d)
56}
57
58python do_image_verity () {
59 import os
60 import subprocess
61 import shutil
62
63 link = d.getVar('VERITY_INPUT_IMAGE')
64 image = os.path.realpath(link)
65
66 verity_image_suffix = d.getVar('VERITY_IMAGE_SUFFIX')
67 verity = '{}{}'.format(image, verity_image_suffix)
68
69 # For better readability the parameter VERITY_BLOCK_SIZE is specified in
70 # bytes. It must be a multiple of the logical sector size which is 512 bytes
71 # in Linux. Make sure that this is the case as otherwise the resulting
72 # issues would be hard to debug later.
73 block_size = int(d.getVar('VERITY_BLOCK_SIZE'))
74 if block_size % 512 != 0:
75 bb.fatal("VERITY_BLOCK_SIZE must be a multiple of 512!")
76
77 salt = d.getVar('VERITY_SALT')
78 if salt == d.getVar('CLASS_VERITY_SALT'):
79 bb.warn("Please overwrite VERITY_SALT with an image specific one!")
80
81 shutil.copyfile(image, verity)
82
83 data_size_blocks, data_size_rest = divmod(os.stat(verity).st_size, block_size)
84 data_blocks = data_size_blocks + (1 if data_size_rest else 0)
85 data_size = data_blocks * block_size
86
87 bb.debug(1, f"data_size_blocks: {data_size_blocks}, {data_size_rest}")
88 bb.debug(1, f"data_size: {data_size}")
89
90 # Create verity image
91 try:
92 output = subprocess.check_output([
93 'veritysetup', 'format',
94 '--no-superblock',
95 '--salt={}'.format(salt),
96 '--data-blocks={}'.format(data_blocks),
97 '--data-block-size={}'.format(block_size),
98 '--hash-block-size={}'.format(block_size),
99 '--hash-offset={}'.format(data_size),
100 verity, verity,
101 ])
102 except subprocess.CalledProcessError as err:
103 bb.fatal('%s returned with %s (%s)' % (err.cmd, err.returncode, err.output))
104
105 try:
106 with open(image + '.verity-info', 'wb') as f:
107 f.write(output)
108 except Exception as err:
109 bb.fatal('Unexpected error %s' % err)
110
111 # Create verity params
112 params = []
113 for line in output.decode('ASCII').splitlines():
114 if not ':' in line:
115 continue
116 k, v = line.split(':', 1)
117 k = k.strip().upper().replace(' ', '_')
118 v = v.strip()
119 bb.debug(1, f"{k} {v}")
120 params.append('VERITY_{}={}'.format(k, v))
121
122 params.append('VERITY_DATA_SECTORS={}'.format(data_size//512))
123
124 try:
125 with open(image + '.verity-params', 'w') as f:
126 f.write('\n'.join(params))
127 except Exception as err:
128 bb.fatal('Unexpected error %s' % err)
129
130 # Create symlinks
131 for suffix in [ verity_image_suffix, '.verity-info', '.verity-params' ]:
132 try:
133 os.remove(link + suffix)
134 except FileNotFoundError:
135 pass
136 os.symlink(os.path.basename(image) + suffix, link + suffix)
137}