summaryrefslogtreecommitdiffstats
path: root/meta-oe/classes/image_types_verity.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta-oe/classes/image_types_verity.bbclass')
-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}