.. SPDX-FileCopyrightText: 2013-2021 Stefano Babic .. SPDX-License-Identifier: GPL-2.0-only Update images from verified source ================================== It is becoming very important that a device must not only be safely updated, but also that it can verify if the delivered image is coming from a known source and it was not corrupted introducing some malware. To achieve this goal, SWUpdate must verify the incoming images. There are several ways to do this. Should the compound image be signed ? Or some parts of it ? Advantages and disadvantages are described in the following chapter. Signing the compound image -------------------------- It looks quite straightforward if the whole compound image is signed. However, this has some heavy drawbacks. It is not possible to know if the image is verified until the whole image is loaded. This means that verification can be done after installing the single images instead of doing it before touching the device. This leads to have some uninstall procedure if part of a not verified image is already installed, procedures that cannot be safe in case of power off letting some unwanted piece of software on the device. Signing the sub-images ---------------------- If each sub-image is signed, the verification is done before calling the corresponding hardware. Only signed images can be installed. Anyway, this remains unbound with the description of the release in sw-description. Even if sw-description is signed, an attacker can mix signed images together generating a new compound image that can be installed as well, because all sub-images are verified. Combining signing sw-description with hash verification ------------------------------------------------------- To avoid the described drawbacks, SWUpdate combines signed sw-description with the verification of hashes for each single image. This means that only sw-description generated by a verified source can be accepted by the installer. sw-description contains hashes for each sub-image to verify that each delivered sub-image really belongs to the release. Choice of algorithm ------------------- The algorithm chosen to sign and verify the sw-descrription file can be selected via menuconfig. Currently, the following mechanisms are implemented: - RSA Public / private key. The private key belongs to the build system, while the public key must be installed on the target. - CMS using certificates - GPG key signing For RSA and CMS algorithms, key or certificate is passed to SWUpdate with the `-k` parameter. Tool to generate keys / certificates ------------------------------------ For RSA and CMS signing, the `openssl` tool is used to generate the keys. This is part of the OpenSSL project. A complete documentation can be found at the `openSSL Website `_. For GPG, `gpg` can be used to generate the keys and to sign the images. A complete documentation can be found at the `GnuPG Website `_. Usage with RSA PKCS#1.5 or RSA PSS ---------------------------------- Generating private and public key ................................. First, the private key must be created: :: openssl genrsa -aes256 -out priv.pem This asks for a passphrase. It is possible to retrieve the passphrase from a file - of course, this must be protected against intrusion. :: openssl genrsa -aes256 -passout file:passout -out priv.pem The private key is used to export the public key with: :: openssl rsa -in priv.pem -out public.pem -outform PEM -pubout "public.pem" contains the key in a format suitable for swupdate. The file can be passed to swupdate at the command line with the -k parameter. How to sign with RSA .................... Signing the image with rsa-pkcs#1.5 is very simple: :: openssl dgst -sha256 -sign priv.pem sw-description > sw-description.sig Signing the image with rsa-pss is also very simple: :: openssl dgst -sha256 -sign priv.pem -sigopt rsa_padding_mode:pss \ -sigopt rsa_pss_saltlen:-2 sw-description > sw-description.sig Usage with certificates and CMS ------------------------------- Generating self-signed certificates ................................... :: openssl req -x509 -newkey rsa:4096 -nodes -keyout mycert.key.pem \ -out mycert.cert.pem -subj "/O=SWUpdate /CN=target" Check the documentation for more information about parameters. The "mycert.key.pem" contains the private key and it is used for signing. It is *not* delivered on the target. The target must have "mycert.cert.pem" installed - this is used by SWUpdate for verification. Using PKI issued certificates ............................. It is also possible to use PKI issued code signing certificates. However, SWUpdate uses OpenSSL library for handling CMS signatures and the library requires the following attributes to be set on the signing certificate: :: keyUsage=digitalSignature extendedKeyUsage=emailProtection It is also possible to completely disable signing certificate key usage checking if this requirement cannot be satisfied. This is controlled by `CONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE` configuration option. How to sign with CMS ..................... Signing the image is simple as in the previous case: :: openssl cms -sign -in sw-description -out sw-description.sig -signer mycert.cert.pem \ -inkey mycert.key.pem -outform DER -nosmimecap -binary Usage with GNU PG ----------------- Generating a new keypair ........................ First, a primary keypair needs to be generated :: gpg --gen-key The generated keys can be listed as follows :: gpg -k Check the documentation for more information about parameters. How to sign with gpg ..................... Signing the image is very simple: :: gpg --batch --output sw-description.sig --detach-sig sw-description For an alternative GnuPG home directory, and if there are multiple keypairs, the following can be used to specify. In this example, the GnuPG home directory is in GPG_HOMEDIR, while the signing key is found in GPG_KEY. :: gpg --batch --homedir "${GPG_HOMEDIR}" --default-key "${GPG_KEY}" --output sw-description.sig --detach-sig sw-description Building a signed SWU image --------------------------- There are two files, sw-description and its signature sw-description.sig. The signature file must always directly follow the description file. Each image inside sw-description must have the attribute "sha256", with the SHA256 sum of the image. If an image does not have the sha256 attribute, the whole compound image results as not verified and SWUpdate stops with an error before starting to install. A simple script to create a signed image can be: :: #!/bin/bash MODE="RSA-PKCS-1.5" PRODUCT_NAME="myproduct" CONTAINER_VER="1.0" IMAGES="rootfs kernel" FILES="sw-description sw-description.sig $IMAGES" #if you use RSA if [ x"$MODE" == "xRSA-PKCS-1.5" ]; then openssl dgst -sha256 -sign priv.pem sw-description > sw-description.sig elif if [ x"$MODE" == "xRSA-PSS" ]; then openssl dgst -sha256 -sign priv.pem -sigopt rsa_padding_mode:pss \ -sigopt rsa_pss_saltlen:-2 sw-description > sw-description.sig elif if [ x"$MODE" == "xGPG" ]; then gpg --batch --homedir "${GPG_HOME_DIR}" --default-key "${GPG_KEY}" \ --output sw-description.sig --detach-sig sw-description else openssl cms -sign -in sw-description -out sw-description.sig -signer mycert.cert.pem \ -inkey mycert.key.pem -outform DER -nosmimecap -binary fi for i in $FILES;do echo $i;done | cpio -ov -H crc > ${PRODUCT_NAME}_${CONTAINER_VER}.swu Example for sw-description with signed image -------------------------------------------- The example applies to a Beaglebone, installing Yocto images: :: software = { version = "0.1.0"; hardware-compatibility: [ "revC"]; images: ( { filename = "core-image-full-cmdline-beaglebone.ext3"; device = "/dev/mmcblk0p2"; type = "raw"; sha256 = "43cdedde429d1ee379a7d91e3e7c4b0b9ff952543a91a55bb2221e5c72cb342b"; } ); scripts: ( { filename = "test.lua"; type = "lua"; sha256 = "f53e0b271af4c2896f56a6adffa79a1ffa3e373c9ac96e00c4cfc577b9bea5f1"; } ); } Running SWUpdate with signed images ----------------------------------- Verification is activated by setting CONFIG_SIGNED_IMAGES in SWUpdate's configuration. If activated, SWUpdate will always check the compound image. For security reasons, it is not possible to disable the check at runtime. For RSA and CMS signing, the -k parameter (public key file) is mandatory and the program stops if the public key is not passed. For GPG signing, CONFIG_SIGALG_GPG needs to be enabled. The GPG key will need to be imported to the device's GnuPG home directory. To do this, the key will need to be exported: :: gpg --export --output You can then copy it onto the device and import it into your public keyring: :: gpg --import To verify that the key has been imported successfully: :: gpg --list-keys SWUpdate will need need to be configured with the following parameters: :: GnuPG Home directory: gpg-home-dir in swupdate.cfg GPGME Protocol: gpgme-protocol in swupdate.cfg: openpgp or cms