Skip to content

Commit

Permalink
Merge pull request rauc#863 from ejoerns/topic/encryption
Browse files Browse the repository at this point in the history
Bundle Encryption Support
  • Loading branch information
jluebbe authored Mar 16, 2022
2 parents c5bde8f + 0e52a95 commit a6cf37f
Show file tree
Hide file tree
Showing 86 changed files with 3,592 additions and 137 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ librauc_la_SOURCES = \
src/checksum.c \
src/config_file.c \
src/context.c \
src/crypt.c \
src/dm.c \
src/install.c \
src/manifest.c \
Expand All @@ -59,6 +60,7 @@ librauc_la_SOURCES = \
include/checksum.h \
include/config_file.h \
include/context.h \
include/crypt.h \
include/dm.h \
include/emmc.h \
include/gpt.h \
Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Features
* chunk-based binary delta updates
* significantly reduce download size
* no extra storage required
* Bundle **encryption** for multiple recipients
* **Bootloader support**:

* `grub <https://www.gnu.org/software/grub/>`_
Expand Down
12 changes: 12 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,23 @@ AC_CONFIG_LINKS([test/bin/preinstall.sh:test/bin/preinstall.sh])
AC_CONFIG_LINKS([test/bin/systeminfo.sh:test/bin/systeminfo.sh])
AC_CONFIG_LINKS([test/broken-manifest.raucm:test/broken-manifest.raucm])
AC_CONFIG_LINKS([test/dummy.verity:test/dummy.verity])
AC_CONFIG_LINKS([test/dummy.encrypted:test/dummy.encrypted])
AC_CONFIG_LINKS([test/dummy.unencrypted:test/dummy.unencrypted])
AC_CONFIG_LINKS([test/good-bundle.raucb:test/good-bundle.raucb])
AC_CONFIG_LINKS([test/good-verity-bundle.raucb:test/good-verity-bundle.raucb])
AC_CONFIG_LINKS([test/good-crypt-bundle-encrypted.raucb:test/good-crypt-bundle-encrypted.raucb])
AC_CONFIG_LINKS([test/good-crypt-bundle-unencrypted.raucb:test/good-crypt-bundle-unencrypted.raucb])
AC_CONFIG_LINKS([test/good-casync-bundle-1.4.raucb:test/good-casync-bundle-1.4.raucb])
AC_CONFIG_LINKS([test/good-casync-bundle-1.5.1.raucb:test/good-casync-bundle-1.5.1.raucb])
AC_CONFIG_LINKS([test/install-content/appfs.img:test/install-content/appfs.img])
AC_CONFIG_LINKS([test/install-content/custom_handler.sh:test/install-content/custom_handler.sh])
AC_CONFIG_LINKS([test/install-content/hook.sh:test/install-content/hook.sh])
AC_CONFIG_LINKS([test/install-content/manifest.raucm:test/install-content/manifest.raucm])
AC_CONFIG_LINKS([test/install-content/manifest.raucm.crypt:test/install-content/manifest.raucm.crypt])
AC_CONFIG_LINKS([test/install-content/rootfs.img:test/install-content/rootfs.img])
AC_CONFIG_LINKS([test/invalid-sig-bundle.raucb:test/invalid-sig-bundle.raucb])
AC_CONFIG_LINKS([test/manifest.raucm:test/manifest.raucm])
AC_CONFIG_LINKS([test/crypt-test.conf:test/crypt-test.conf])
AC_CONFIG_LINKS([test/minimal-test.conf:test/minimal-test.conf])
AC_CONFIG_LINKS([test/openssl-ca/dev/autobuilder-1.cert.pem:test/openssl-ca/dev/autobuilder-1.cert.pem])
AC_CONFIG_LINKS([test/openssl-ca/dev/autobuilder-2.cert.pem:test/openssl-ca/dev/autobuilder-2.cert.pem])
Expand Down Expand Up @@ -245,6 +251,12 @@ AC_CONFIG_LINKS([test/openssl-ca/web/client-1.cert.pem:test/openssl-ca/web/clien
AC_CONFIG_LINKS([test/openssl-ca/web/private/client-1.pem:test/openssl-ca/web/private/client-1.pem])
AC_CONFIG_LINKS([test/openssl-ca/web/private/server.pem:test/openssl-ca/web/private/server.pem])
AC_CONFIG_LINKS([test/openssl-ca/web/server.chain.pem:test/openssl-ca/web/server.chain.pem])
AC_CONFIG_LINKS([test/openssl-enc/keys/ecc/certs.pem:test/openssl-enc/keys/ecc/certs.pem])
AC_CONFIG_LINKS([test/openssl-enc/keys/rsa-4096/cert-000.pem:test/openssl-enc/keys/rsa-4096/cert-000.pem])
AC_CONFIG_LINKS([test/openssl-enc/keys/rsa-4096/cert-001.pem:test/openssl-enc/keys/rsa-4096/cert-001.pem])
AC_CONFIG_LINKS([test/openssl-enc/keys/rsa-4096/certs.pem:test/openssl-enc/keys/rsa-4096/certs.pem])
AC_CONFIG_LINKS([test/openssl-enc/keys/rsa-4096/private-key-000.pem:test/openssl-enc/keys/rsa-4096/private-key-000.pem])
AC_CONFIG_LINKS([test/openssl-enc/keys/rsa-4096/private-key-005.pem:test/openssl-enc/keys/rsa-4096/private-key-005.pem])
AC_CONFIG_LINKS([test/rauc.t:test/rauc.t])
AC_CONFIG_LINKS([test/rootfs.raucs:test/rootfs.raucs])
AC_CONFIG_LINKS([test/sharness.sh:test/sharness.sh])
Expand Down
150 changes: 150 additions & 0 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,156 @@ higher round-trip-time (RTT) lead to longer installation times.
This can be compensated somewhat by using a HTTP/2 server, as this supports
multiplexing and better connection reuse.
.. _sec-encryption:
Bundle Encryption
-----------------
RAUC supports encrypting the bundle to one or more recipients (public keys).
The implementation of the crypt bundle format is based on the verity bundle
format (which uses Linux's dm-verity module).
It works by symmetrically encrypting the bundle payload and using
Linux's dm-crypt module to decrypt this on-demand.
The symmetric encryption key is contained in the manifest, which
itself is (asymmetrically) encrypted to a set of recipients.
Similar to the verity format, the crypt format can also be used
with HTTP streaming.
To use encryption, some prerequisites need to be fulfilled:
- create bundle using the crypt format
- enable dm-crypt support in the target's kernel
- have private key accessible on the target via path or PKCS#11-URI
Creating an encrypted bundle has two main steps:
- encrypting the payload with ``rauc bundle`` using a manifest configured for the crypt format
- encrypting the manifest with the payload encryption key for specific recipients with ``rauc encrypt``
We've separated these steps to support more flexibility regarding decryption keys.
Some possible workflows are described in :ref:`sec-encryption-workflows`.
The first step can be performed by a build system, very similar to how un-encrypted bundles are created.
RAUC generates a random key for symmetric AES-256 encryption of the bundle payload (the SquashFS).
The encrypted payload is then protected against modification with dm-verity (see the verity format for details).
The AES key is stored (*as plain text*) in the signed manifest.
The second step needs to be performed before publishing the bundle.
You need to provide (one or more) recipient certificates,
which are used to encrypt the signed manifest.
The already encrypted payload is reused unmodified.
Any of the corresponding private keys can then be used by RAUC to first decrypt the
manifest, which then contains the key needed to decrypt the (SquashFS) payload.
.. code-block::
$ rauc encrypt --to=recipient-certs.pem unencrypted-crypt-bundle.raucb encrypted-crypt-bundle.raucb
.. note::
To encrypt for a larger number of recipients, the recipient certificates can be
concatenated and provided as a single file in the ``--to`` argument.
Also note that the certificates used for encryption don't need to be part of
the signing PKI.
To inspect an encrypted bundle on your build host, you need to provide the
encryption key via the ``--key`` argument::
$ rauc info --key=/path/to/private-key.pem --keyring=/path/to/keyring.pem encrypted-crypt-bundle.raucb
Compatible: 'Example Target'
Version: '2022.03-2'
Description: '(null)'
Build: '(null)'
Hooks: ''
Bundle Format: crypt [encrypted CMS]
Crypt Key: '<hidden>'
Verity Salt: '18bfbba9f129f97b6bca4aa0645db61feac2511fa940f8169c659601849de38a'
Verity Hash: '505d1d57bf9b280b88b023fb74d6a847c2fb419d70609b91460d5e42c465b6dd'
Verity Size: 4096
[…]
Before installing an encrypted RAUC Bundle on the target, you need to configure
the location of the target's private key in the system.conf:
.. code-block:: cfg
:emphasize-lines: 4,5
[system]
compatible=Example Target
[encryption]
key=pkcs11:token=rauc;object=private-key-1
The installation command then does not differ from the installation of an
unencrypted bundle::
# rauc install encrypted-bundle.raucb
.. _sec-encryption-workflows:
Encryption Key Workflows
~~~~~~~~~~~~~~~~~~~~~~~~
Shared Key
^^^^^^^^^^
All devices share a single key pair, perhaps store in the initial image
installed in the factory.
While a single key shared across all devices is simple to manage, it's
usually not feasible to revoke or replace in case it is compromised.
This means that an attacker requires access to only a single device to be able
to decrypt any further updates.
Note that this does *not* allow the attacker to bypass the bundle authentication.
Group Key
^^^^^^^^^
In this case, a group of devices (perhaps a production batch or for a single customer)
shares one key-pair.
Depending on the circumstances and impact, it might be easier to revoke or replace
it in case it is compromised, at least compared to the shared key approach.
Unique Per-Device Key
^^^^^^^^^^^^^^^^^^^^^
Each device has its own key, possibly protected using a TPM, HSM or TEE.
These keys could be generated on the device in the factory and the corresponding
public key stored in some device database.
In some scenarios, devices already have a unique key (and certificate)
for access to a server or VPN.
Depending on how these keys are configured, it may be possible to reuse
them for bundle encryption as well.
If any device key is compromised, it can be revoked and removed from the set
of recipients for the next update.
Accordingly, only the single compromised device will no longer be able to decrypt
updates.
Scalability
~~~~~~~~~~~
For each recipient specified to ``rauc encrypt``, the bundle size will increase
by a small amount (actual sizes depend on certificate metadata):
- RSA 4096: ~620 bytes
- ECC: ~250 bytes
With very large numbers of keys, this would result in bundles where the encryption overhead
becomes problematic.
To mitigate this issue, the set of keys can be split into multiple subsets, where the same
bundle is encrypted once per subset.
Then, depending on how each device's key is assigned to a subset, it would need to be provided
with the corresponding encrypted bundle.
As the encrypted payload is still the identical for each subset's bundle and only the encrypted
CMS structure (containing the signed manifest) differs, the payload needs to be stored only once.
If needed, this could be implemented in a web application or using a reflink-capable Linux filesystem.
Data Storage and Migration
--------------------------
Expand Down
4 changes: 4 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ Key Features of RAUC

* No intermediate storage on target required.

* Optional **encryption** of update bundles

* Encryption for a single or multiple recipients (public keys) supported

* **Flexible and customizable** redundancy/storage setup

.. image:: images/rauc_update_cases.svg
Expand Down
3 changes: 2 additions & 1 deletion docs/integration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ In kernel Kconfig you have to enable the following options:
CONFIG_SQUASHFS=y
CONFIG_CRYPTO_SHA256=y
For streaming support, you have to add `CONFIG_BLK_DEV_NBD`.
For streaming support, you have to add ``CONFIG_BLK_DEV_NBD``.
For encryption support, you have to add ``CONFIG_DM_CRYPT``.

.. note::
These drivers may also be loaded as modules. Kernel versions v5.0 to v5.7
Expand Down
55 changes: 49 additions & 6 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,22 @@ For more information about using the streaming support of RAUC, refer to
This option can be used to set the path of the CA certificate which should be
used instead of the system wide store of trusted TLS/HTTPS certificates.

**[encryption]**

The ``encryption`` section contains information required to decrypt a 'crypt'
bundle.
For more information about encrypted RAUC bundle bundles, refer to
:ref:`sec-encryption`.

``key``
Path or PKCS#11 URL for the private key used to decrypt bundles.
This is mandatory for decrypting encrypted bundles.

``cert``
Path or PKCS#11 URL for the certificate matching the encryption key.
This is optional but allows to speed up key lookup and thus is especially
useful for larger number of recipients.

**[casync] section**

The ``casync`` section contains casync-related settings.
Expand Down Expand Up @@ -479,6 +495,10 @@ A valid RAUC manifest file must be named ``manifest.raucm``.
RAUC determines this value automatically, so it should be left unspecified
when preparing a manifest for bundle creation.

``crypt-key``
The encryption key of the dm-crypt.
RAUC generates the key automatically when creating a `crypt` bundle.

**[hooks] section**

``filename``
Expand Down Expand Up @@ -532,16 +552,18 @@ A valid RAUC manifest file must be named ``manifest.raucm``.
Bundle Formats
--------------

RAUC currently supports two bundle formats (``plain`` and ``verity``) and
additional formats could be added to support features such as encryption.
RAUC currently supports three bundle formats (``plain``, ``verity`` and
``crypt``) and additional formats could be added if required.
Version 1.4 (released on 2020-06-20) and earlier only supported a single format
which is now named ``plain``, which should be used as long as compatibility to
those versions is required.

The ``verity`` format was added to prepare for future use cases (such as
network streaming and encryption), for better parallelization of installation
with hash verification and to detect modification of the bundle during
installation.
The ``verity`` format was added to support new use cases like network
streaming, for better parallelization of installation with hash verification
and to detect modification of the bundle during installation.

The ``crypt`` format is an extension to the ``verity`` format that allows full
encryption of the bundle.

The bundle format is detected when reading a bundle and checked against the set
of allowed formats configured in the ``system.conf`` (see :ref:`bundle-formats
Expand Down Expand Up @@ -594,6 +616,27 @@ normal users on their development hosts.
It this case, the same mechanism for ensuring exclusive access as with plain
bundles is used.

.. _sec_ref_format_crypt:

crypt Format
~~~~~~~~~~~~

In this case, a bundle consists of:

* SquashFS filesystem containing manifest (without verity metadata or crypt key) and images,
encrypted using dm-crypt mode aes-cbc-plain64
* dm-verity hash tree over the encrypted SquashFS filesystem
* CMS signature over an inline manifest (with verity metadata and crypt key),
encrypted to a set of recipients
* size of the encrypted CMS structure

In addition to the metadata used by the verity format,
the manifest for this format contains the AES-256 key required for decryption of the SquashFS payload.
To protect the payload key, the signed manifest is then encrypted.

During installation, the kernel's crypt and verity device mapper targets are used on top of the
loopback or network block device to authenticate and then decrypt each payload block as needed.

.. _sec_ref_external_signing:

External Signing and PKI
Expand Down
18 changes: 18 additions & 0 deletions include/bundle.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef enum {
R_BUNDLE_ERROR_PAYLOAD,
R_BUNDLE_ERROR_FORMAT,
R_BUNDLE_ERROR_VERITY,
R_BUNDLE_ERROR_CRYPT,
} RBundleError;

typedef struct {
Expand All @@ -40,13 +41,15 @@ typedef struct {
GInputStream *stream;

goffset size;
GBytes *enveloped_data;
GBytes *sigdata;
gchar *mount_point;
RaucManifest *manifest;
gboolean verification_disabled;
gboolean signature_verified;
gboolean payload_verified;
gboolean exclusive_verified;
gboolean was_encrypted;
gchar *exclusive_check_error;
STACK_OF(X509) *verified_chain;
} RaucBundle;
Expand Down Expand Up @@ -86,6 +89,9 @@ G_GNUC_WARN_UNUSED_RESULT;
*
* This will verify and check the bundle content.
*
* For bundle formats with detached CMS (i.e. 'verity' or 'crypt'),
* this will also initialize the manifest.
*
* @param bundlename filename of the bundle to check
* @param bundle return location for a RaucBundle struct.
* This will contain all bundle information obtained by
Expand Down Expand Up @@ -219,6 +225,18 @@ G_GNUC_WARN_UNUSED_RESULT;
gboolean create_casync_bundle(RaucBundle *bundle, const gchar *outbundle, GError **error)
G_GNUC_WARN_UNUSED_RESULT;

/**
* Encrypt a crypt bundle.
*
* @param bundle RaucBundle struct as returned by check_bundle()
* @param outbundle output location for encrypted crypt bundle
* @param error Return location for a GError
*
* @return TRUE on success, FALSE if an error occurred
*/
gboolean encrypt_bundle(RaucBundle *bundle, const gchar *outbundle, GError **error)
G_GNUC_WARN_UNUSED_RESULT;

/**
* Mount a bundle.
*
Expand Down
4 changes: 4 additions & 0 deletions include/config_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ typedef struct {
gchar *streaming_tls_key;
gchar *streaming_tls_ca;

/* encryption */
gchar *encryption_key;
gchar *encryption_cert;

GHashTable *slots;
} RaucConfig;

Expand Down
Loading

0 comments on commit a6cf37f

Please sign in to comment.