aboutsummaryrefslogtreecommitdiffstats
path: root/meta-oe/classes
diff options
context:
space:
mode:
Diffstat (limited to 'meta-oe/classes')
-rw-r--r--meta-oe/classes/gitpkgv.bbclass14
-rw-r--r--meta-oe/classes/image_types_sparse.bbclass11
-rw-r--r--meta-oe/classes/image_types_verity.bbclass137
-rw-r--r--meta-oe/classes/signing.bbclass316
4 files changed, 466 insertions, 12 deletions
diff --git a/meta-oe/classes/gitpkgv.bbclass b/meta-oe/classes/gitpkgv.bbclass
index 180421ed35..5ab507969c 100644
--- a/meta-oe/classes/gitpkgv.bbclass
+++ b/meta-oe/classes/gitpkgv.bbclass
@@ -25,17 +25,17 @@
#
# inherit gitpkgv
#
-# PV = "1.0+gitr${SRCPV}" # expands to something like 1.0+gitr3+4c1c21d7dbbf93b0df336994524313dfe0d4963b
-# PKGV = "1.0+gitr${GITPKGV}" # expands also to something like 1.0+gitr31337+4c1c21d7d
+# PV = "1.0+git" # expands to 1.0+git
+# PKGV = "1.0+git${GITPKGV}" # expands also to something like 1.0+git31337+4c1c21d7d
#
# or
#
# inherit gitpkgv
#
-# PV = "1.0+gitr${SRCPV}" # expands to something like 1.0+gitr3+4c1c21d7dbbf93b0df336994524313dfe0d4963b
-# PKGV = "${GITPKGVTAG}" # expands to something like 1.0-31337+g4c1c21d
-# if there is tag v1.0 before this revision or
-# ver1.0-31337+g4c1c21d if there is tag ver1.0
+# PV = "1.0+git" # expands to 1.0+git
+# PKGV = "${GITPKGVTAG}" # expands to something like 1.0-31337+g4c1c21d
+# if there is tag v1.0 before this revision or
+# ver1.0-31337+g4c1c21d if there is tag ver1.0
GITPKGV = "${@get_git_pkgv(d, False)}"
GITPKGVTAG = "${@get_git_pkgv(d, True)}"
@@ -56,7 +56,7 @@ def gitpkgv_drop_tag_prefix(d, version):
def get_git_pkgv(d, use_tags):
import os
import bb
- from pipes import quote
+ from shlex import quote
src_uri = d.getVar('SRC_URI').split()
fetcher = bb.fetch2.Fetch(src_uri, d)
diff --git a/meta-oe/classes/image_types_sparse.bbclass b/meta-oe/classes/image_types_sparse.bbclass
index 69e24cbb79..d6ea68968e 100644
--- a/meta-oe/classes/image_types_sparse.bbclass
+++ b/meta-oe/classes/image_types_sparse.bbclass
@@ -8,9 +8,10 @@ inherit image_types
SPARSE_BLOCK_SIZE ??= "4096"
CONVERSIONTYPES += "sparse"
-CONVERSION_CMD:sparse() {
- INPUT="${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}"
- truncate --no-create --size=%${SPARSE_BLOCK_SIZE} "$INPUT"
- img2simg -s "$INPUT" "$INPUT.sparse" ${SPARSE_BLOCK_SIZE}
-}
+
+CONVERSION_CMD:sparse = " \
+ truncate --no-create --size=%${SPARSE_BLOCK_SIZE} "${IMAGE_NAME}.${type}"; \
+ img2simg -s "${IMAGE_NAME}.${type}" "${IMAGE_NAME}.${type}.sparse" ${SPARSE_BLOCK_SIZE}; \
+ "
+
CONVERSION_DEPENDS_sparse = "android-tools-native"
diff --git a/meta-oe/classes/image_types_verity.bbclass b/meta-oe/classes/image_types_verity.bbclass
new file mode 100644
index 0000000000..b42217c453
--- /dev/null
+++ b/meta-oe/classes/image_types_verity.bbclass
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright Pengutronix <yocto@pengutronix.de>
+#
+
+# Support generating 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.
+
+inherit image-artifact-names
+
+do_image_verity[depends] += "cryptsetup-native:do_populate_sysroot"
+
+CLASS_VERITY_SALT = "4e5f0d9b6ccac5e843598d4e4545046232b48451a399acb2106822b43679b375"
+VERITY_SALT ?= "${CLASS_VERITY_SALT}"
+VERITY_BLOCK_SIZE ?= "4096"
+VERITY_IMAGE_FSTYPE ?= "ext4"
+VERITY_IMAGE_SUFFIX ?= ".verity"
+VERITY_INPUT_IMAGE ?= "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.${VERITY_IMAGE_FSTYPE}"
+
+IMAGE_TYPEDEP:verity = "${VERITY_IMAGE_FSTYPE}"
+IMAGE_TYPES_MASKED += "verity"
+
+python __anonymous() {
+ if 'verity' not in d.getVar('IMAGE_FSTYPES'):
+ return
+
+ dep_task = 'do_image_{}'.format(d.getVar('VERITY_IMAGE_FSTYPE').replace('-', '_'))
+ bb.build.addtask('do_image_verity', 'do_image_complete', dep_task, d)
+}
+
+python do_image_verity () {
+ import os
+ import subprocess
+ import shutil
+
+ link = d.getVar('VERITY_INPUT_IMAGE')
+ image = os.path.realpath(link)
+
+ verity_image_suffix = d.getVar('VERITY_IMAGE_SUFFIX')
+ verity = '{}{}'.format(image, verity_image_suffix)
+
+ # For better readability the parameter VERITY_BLOCK_SIZE is specified in
+ # bytes. It must be a multiple of the logical sector size which is 512 bytes
+ # in Linux. Make sure that this is the case as otherwise the resulting
+ # issues would be hard to debug later.
+ block_size = int(d.getVar('VERITY_BLOCK_SIZE'))
+ if block_size % 512 != 0:
+ bb.fatal("VERITY_BLOCK_SIZE must be a multiple of 512!")
+
+ salt = d.getVar('VERITY_SALT')
+ if salt == d.getVar('CLASS_VERITY_SALT'):
+ bb.warn("Please overwrite VERITY_SALT with an image specific one!")
+
+ shutil.copyfile(image, verity)
+
+ data_size_blocks, data_size_rest = divmod(os.stat(verity).st_size, block_size)
+ data_blocks = data_size_blocks + (1 if data_size_rest else 0)
+ data_size = data_blocks * block_size
+
+ bb.debug(1, f"data_size_blocks: {data_size_blocks}, {data_size_rest}")
+ bb.debug(1, f"data_size: {data_size}")
+
+ # Create verity image
+ try:
+ output = subprocess.check_output([
+ 'veritysetup', 'format',
+ '--no-superblock',
+ '--salt={}'.format(salt),
+ '--data-blocks={}'.format(data_blocks),
+ '--data-block-size={}'.format(block_size),
+ '--hash-block-size={}'.format(block_size),
+ '--hash-offset={}'.format(data_size),
+ verity, verity,
+ ])
+ except subprocess.CalledProcessError as err:
+ bb.fatal('%s returned with %s (%s)' % (err.cmd, err.returncode, err.output))
+
+ try:
+ with open(image + '.verity-info', 'wb') as f:
+ f.write(output)
+ except Exception as err:
+ bb.fatal('Unexpected error %s' % err)
+
+ # Create verity params
+ params = []
+ for line in output.decode('ASCII').splitlines():
+ if not ':' in line:
+ continue
+ k, v = line.split(':', 1)
+ k = k.strip().upper().replace(' ', '_')
+ v = v.strip()
+ bb.debug(1, f"{k} {v}")
+ params.append('VERITY_{}={}'.format(k, v))
+
+ params.append('VERITY_DATA_SECTORS={}'.format(data_size//512))
+
+ try:
+ with open(image + '.verity-params', 'w') as f:
+ f.write('\n'.join(params))
+ except Exception as err:
+ bb.fatal('Unexpected error %s' % err)
+
+ # Create symlinks
+ for suffix in [ verity_image_suffix, '.verity-info', '.verity-params' ]:
+ try:
+ os.remove(link + suffix)
+ except FileNotFoundError:
+ pass
+ os.symlink(os.path.basename(image) + suffix, link + suffix)
+}
diff --git a/meta-oe/classes/signing.bbclass b/meta-oe/classes/signing.bbclass
new file mode 100644
index 0000000000..f52d861b76
--- /dev/null
+++ b/meta-oe/classes/signing.bbclass
@@ -0,0 +1,316 @@
+#
+# Copyright Jan Luebbe <jlu@pengutronix.de>
+#
+# SPDX-License-Identifier: MIT
+#
+
+# This class provides a common workflow to use asymmetric (i.e. RSA) keys to
+# sign artifacts. Usually, the keys are either stored as simple files in the
+# file system or on an HSM (Hardware Security Module). While files are easy to
+# use, it's hard to verify that no copies of the private key have been made
+# and only authorized persons are able to use the key. Use of an HSM addresses
+# these risks by only allowing use of the key via an API (often PKCS #11). The
+# standard way of referring to a specific key in an HSM are PKCS #11 URIs (RFC
+# 7512).
+#
+# Many software projects support signing using PKCS #11 keys, but configuring
+# this is very project specific. Furthermore, as physical HSMs are not very
+# widespread, testing code signing in CI is not simple. To solve this at the
+# build system level, this class takes the approach of always using PKCS #11 at
+# the recipe level. For cases where the keys are available as files (i.e. test
+# keys in CI), they are imported into SoftHSM (a HSM emulation library).
+#
+# Recipes access the available keys via a specific role. So, depending on
+# whether we're building during development or for release, a given role can
+# refer to different keys.
+# Each key recipe PROVIDES a virtual package corresponding to the role, allowing
+# the user to select one of multiple keys for a role when needed.
+#
+# For use with a real HSM, a PKCS #11 URI can be set (i.e. in local.conf) to
+# override the SoftHSM key with the real one:
+#
+# SIGNING_PKCS11_URI[fit] = "pkcs11:serial=DENK0200554;object=ptx-dev-rauc&pin-value=123456"
+# SIGNING_PKCS11_MODULE[fit] = "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so"
+#
+# Examples for defining roles and importing keys:
+#
+# meta-code-signing/recipes-security/signing-keys/dummy-rsa-key-native.bb
+# meta-code-signing-demo/recipes-security/ptx-dev-keys/ptx-dev-keys-native_git.bb
+#
+# Examples for using keys for signing:
+#
+# meta-code-signing-demo/recipes-security/fit-image/linux-fit-image.bb
+# meta-code-signing-demo/recipes-core/bundles/update-bundle.bb
+#
+# Examples for using keys for authentication:
+#
+# meta-code-signing-demo/recipes-security/fit-image/barebox_%.bbappend
+# meta-code-signing-demo/recipes-core/rauc/rauc_%.bbappend
+#
+# Examples for using keys for both signing and authentication:
+#
+# meta-code-signing-demo/recipes-kernel/linux/linux-yocto_6.1.bbappend
+
+SIGNING_PKCS11_URI ?= ""
+SIGNING_PKCS11_MODULE ?= ""
+
+DEPENDS += "softhsm-native libp11-native opensc-native openssl-native"
+
+def signing_class_prepare(d):
+ import os.path
+
+ def export(role, k, v):
+ k = k % (role, )
+ d.setVar(k, v)
+ d.setVarFlag(k, "export", "1")
+
+ roles = set()
+ roles |= (d.getVarFlags("SIGNING_PKCS11_URI") or {}).keys()
+ roles |= (d.getVarFlags("SIGNING_PKCS11_MODULE") or {}).keys()
+ for role in roles:
+ if not set(role).issubset("abcdefghijklmnopqrstuvwxyz0123456789_"):
+ bb.fatal("key role name '%s' must consist of only [a-z0-9_]" % (role,))
+
+ pkcs11_uri = d.getVarFlag("SIGNING_PKCS11_URI", role) or d.getVar("SIGNING_PKCS11_URI")
+ if not pkcs11_uri.startswith("pkcs11:"):
+ bb.fatal("URI for key role '%s' must start with 'pkcs11:'" % (role,))
+
+ pkcs11_module = d.getVarFlag("SIGNING_PKCS11_MODULE", role) or d.getVar("SIGNING_PKCS11_MODULE")
+ if not os.path.isfile(pkcs11_module):
+ bb.fatal("module path for key role '%s' must be an existing file" % (role,))
+
+ if pkcs11_uri and not pkcs11_module:
+ bb.warn("SIGNING_PKCS11_URI[%s] is set without SIGNING_PKCS11_MODULE[%s]" % (role, role))
+ if pkcs11_module and not pkcs11_uri:
+ bb.warn("SIGNING_PKCS11_MODULE[%s] is set without SIGNING_PKCS11_URI[%s]" % (role, role))
+
+ export(role, "SIGNING_PKCS11_URI_%s_", pkcs11_uri)
+ export(role, "SIGNING_PKCS11_MODULE_%s_", pkcs11_module)
+
+signing_pkcs11_tool() {
+ pkcs11-tool --module "${STAGING_LIBDIR_NATIVE}/softhsm/libsofthsm2.so" --login --pin 1111 $*
+}
+
+signing_import_prepare() {
+ export _SIGNING_ENV_FILE_="${B}/meta-signing.env"
+ rm -f "$_SIGNING_ENV_FILE_"
+
+ export SOFTHSM2_CONF="${B}/softhsm2.conf"
+ export SOFTHSM2_DIR="${B}/softhsm2.tokens"
+ export SOFTHSM2_MOD="${STAGING_LIBDIR_NATIVE}/softhsm/libsofthsm2.so"
+
+ echo "directories.tokendir = $SOFTHSM2_DIR" > "$SOFTHSM2_CONF"
+ echo "objectstore.backend = db" >> "$SOFTHSM2_CONF"
+ rm -rf "$SOFTHSM2_DIR"
+ mkdir -p "$SOFTHSM2_DIR"
+
+ softhsm2-util --module $SOFTHSM2_MOD --init-token --free --label ${PN} --pin 1111 --so-pin 222222
+}
+
+signing_import_define_role() {
+ local role="${1}"
+ case "${1}" in
+ (*[!a-z0-9_]*) false;;
+ (*) true;;
+ esac || bbfatal "invalid role name '${1}', must consist of [a-z0-9_]"
+
+ echo "_SIGNING_PKCS11_URI_${role}_=\"pkcs11:token=${PN};object=$role;pin-value=1111\"" >> $_SIGNING_ENV_FILE_
+ echo "_SIGNING_PKCS11_MODULE_${role}_=\"softhsm\"" >> $_SIGNING_ENV_FILE_
+}
+
+# signing_import_cert_from_der <role> <der>
+#
+# Import a certificate from DER file to a role. To be used
+# with SoftHSM.
+signing_import_cert_from_der() {
+ local role="${1}"
+ local der="${2}"
+
+ signing_pkcs11_tool --type cert --write-object "${der}" --label "${role}"
+}
+
+# signing_import_cert_from_pem <role> <pem>
+#
+# Import a certificate from PEM file to a role. To be used
+# with SoftHSM.
+signing_import_cert_from_pem() {
+ local role="${1}"
+ local pem="${2}"
+
+ openssl x509 \
+ -in "${pem}" -inform pem -outform der |
+ signing_pkcs11_tool --type cert --write-object /proc/self/fd/0 --label "${role}"
+}
+
+# signing_import_pubkey_from_der <role> <der>
+#
+# Import a public key from DER file to a role. To be used with SoftHSM.
+signing_import_pubkey_from_der() {
+ local role="${1}"
+ local der="${2}"
+
+ signing_pkcs11_tool --type pubkey --write-object "${der}" --label "${role}"
+}
+
+# signing_import_pubkey_from_pem <role> <pem>
+#
+# Import a public key from PEM file to a role. To be used with SoftHSM.
+signing_import_pubkey_from_pem() {
+ local openssl_keyopt
+ local role="${1}"
+ local pem="${2}"
+
+ if [ -n "${IMPORT_PASS_FILE}" ]; then
+ openssl pkey \
+ -passin "file:${IMPORT_PASS_FILE}" \
+ -in "${pem}" -inform pem -pubout -outform der
+ else
+ openssl pkey \
+ -in "${pem}" -inform pem -pubout -outform der
+ fi |
+ signing_pkcs11_tool --type pubkey --write-object /proc/self/fd/0 --label "${role}"
+}
+
+# signing_import_privkey_from_der <role> <der>
+#
+# Import a private key from DER file to a role. To be used with SoftHSM.
+signing_import_privkey_from_der() {
+ local role="${1}"
+ local der="${2}"
+ signing_pkcs11_tool --type privkey --write-object "${der}" --label "${role}"
+}
+
+# signing_import_privkey_from_pem <role> <pem>
+#
+# Import a private key from PEM file to a role. To be used with SoftHSM.
+signing_import_privkey_from_pem() {
+ local openssl_keyopt
+ local role="${1}"
+ local pem="${2}"
+
+ if [ -n "${IMPORT_PASS_FILE}" ]; then
+ openssl pkey \
+ -passin "file:${IMPORT_PASS_FILE}" \
+ -in "${pem}" -inform pem -outform der
+ else
+ openssl pkey \
+ -in "${pem}" -inform pem -outform der
+ fi |
+ signing_pkcs11_tool --type privkey --write-object /proc/self/fd/0 --label "${role}"
+}
+
+# signing_import_key_from_pem <role> <pem>
+#
+# Import a private and public key from PEM file to a role. To be used
+# with SoftHSM.
+signing_import_key_from_pem() {
+ local role="${1}"
+ local pem="${2}"
+
+ signing_import_pubkey_from_pem "${role}" "${pem}"
+ signing_import_privkey_from_pem "${role}" "${pem}"
+}
+
+signing_import_finish() {
+ echo "loaded objects:"
+ signing_pkcs11_tool --list-objects
+}
+
+signing_import_install() {
+ install -d ${D}${localstatedir}/lib/softhsm/tokens/${PN}
+ install -m 600 -t ${D}${localstatedir}/lib/softhsm/tokens/${PN} ${B}/softhsm2.tokens/*/*
+ install -d ${D}${localstatedir}/lib/meta-signing.env.d
+ install -m 644 "${B}/meta-signing.env" ${D}${localstatedir}/lib/meta-signing.env.d/${PN}
+}
+
+signing_prepare() {
+ export OPENSSL_MODULES="${STAGING_LIBDIR_NATIVE}/ossl-modules"
+ export OPENSSL_ENGINES="${STAGING_LIBDIR_NATIVE}/engines-3"
+ export OPENSSL_CONF="${STAGING_LIBDIR_NATIVE}/ssl-3/openssl.cnf"
+ export SSL_CERT_DIR="${STAGING_LIBDIR_NATIVE}/ssl-3/certs"
+ export SSL_CERT_FILE="${STAGING_LIBDIR_NATIVE}/ssl-3/cert.pem"
+
+ if [ -f ${OPENSSL_CONF} ]; then
+ echo "Using '${OPENSSL_CONF}' for OpenSSL configuration"
+ else
+ echo "Missing 'openssl.cnf' at '${STAGING_ETCDIR_NATIVE}/ssl'"
+ return 1
+ fi
+ if [ -d ${OPENSSL_MODULES} ]; then
+ echo "Using '${OPENSSL_MODULES}' for OpenSSL run-time modules"
+ else
+ echo "Missing OpenSSL module directory at '${OPENSSL_MODULES}'"
+ return 1
+ fi
+ if [ -d ${OPENSSL_ENGINES} ]; then
+ echo "Using '${OPENSSL_ENGINES}' for OpenSSL run-time PKCS#11 modules"
+ else
+ echo "Missing OpenSSL PKCS11 engine directory at '${OPENSSL_ENGINES}'"
+ return 1
+ fi
+
+ export SOFTHSM2_CONF="${WORKDIR}/softhsm2.conf"
+ export SOFTHSM2_DIR="${STAGING_DIR_NATIVE}/var/lib/softhsm/tokens"
+
+ echo "directories.tokendir = $SOFTHSM2_DIR" > "$SOFTHSM2_CONF"
+ echo "objectstore.backend = db" >> "$SOFTHSM2_CONF"
+
+ for env in $(ls "${STAGING_DIR_NATIVE}/var/lib/meta-signing.env.d"); do
+ . "${STAGING_DIR_NATIVE}/var/lib/meta-signing.env.d/$env"
+ done
+}
+# make sure these functions are exported
+signing_prepare[vardeps] += "signing_get_uri signing_get_module"
+
+signing_use_role() {
+ local role="${1}"
+
+ export PKCS11_MODULE_PATH="$(signing_get_module $role)"
+ export PKCS11_URI="$(signing_get_uri $role)"
+
+ if [ -z "$PKCS11_MODULE_PATH" ]; then
+ echo "No PKCS11_MODULE_PATH found for role '${role}'"
+ exit 1
+ fi
+ if [ -z "$PKCS11_URI" ]; then
+ echo "No PKCS11_URI found for role '${role}'"
+ exit 1
+ fi
+}
+
+signing_get_uri() {
+ local role="${1}"
+
+ # prefer local configuration
+ eval local uri="\$SIGNING_PKCS11_URI_${role}_"
+ if [ -n "$uri" ]; then
+ echo "$uri"
+ return
+ fi
+
+ # fall back to softhsm
+ eval echo "\$_SIGNING_PKCS11_URI_${role}_"
+}
+
+signing_get_module() {
+ local role="${1}"
+
+ # prefer local configuration
+ eval local module="\$SIGNING_PKCS11_MODULE_${role}_"
+ if [ -n "$module" ]; then
+ echo "$module"
+ return
+ fi
+
+ # fall back to softhsm
+ eval local module="\$_SIGNING_PKCS11_MODULE_${role}_"
+ if [ "$module" = "softhsm" ]; then
+ echo "${STAGING_LIBDIR_NATIVE}/softhsm/libsofthsm2.so"
+ else
+ echo "$module"
+ fi
+}
+
+python () {
+ signing_class_prepare(d)
+}