.. SPDX-FileCopyrightText: 2013-2021 Stefano Babic <stefano.babic@swupdate.org>
.. 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 <https://www.openssl.org/docs/manmaster/man1/openssl.html>`_.

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 <https://www.gnupg.org>`_.

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 <keyid> --output <public key>

You can then copy it onto the device and import it into your public keyring:

::
        gpg --import <public key>

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
