RAUC signing
RAUC (Robust Auto-Update Controller) signing enables secure software updates by generating and managing cryptographic keys and certificates within a Public Key Infrastructure (PKI). This section outlines the steps to configure a product to support RAUC signing, including setting up the necessary PKI hierarchy and enabling the RAUC bundle signing operation with ephemeral keys and certificates. Using unique, one-time certificates for each bundle enhances security by allowing individual bundles to be revoked if needed.
Prerequisites¶
- Familiarity with PKI concepts and product configuration processes.
- Access to the Product Configuration Guide for detailed instructions on product setup.
- Predefined PKI profiles for RAUC signing, as described in RAUC Signing Template Examples.
Configuring RAUC Signing¶
To enable RAUC signing, you must create a product with a PKI hierarchy that supports the generation of a Certificate Authority (CA) and ephemeral signing keys. The RAUC bundles are signed using unique, one-time keys and certificates generated on demand, ensuring secure and revocable updates.
Step-by-Step Configuration¶
-
Create a Product with RAUC Signing Support:
- Configure a product that includes a PKI hierarchy for RAUC signing and enables the RAUC bundle signing operation.
- Ensure the product is set up to use the
RaucBundleSigningWithOneTimeKeyoperation, which generates ephemeral keys and certificates for each signing operation.
-
Set Up the PKI Hierarchy:
- During product creation, a PKI tree is generated, including a Certificate Authority (CA) stored in the HSM.
- The CA issues short-lived certificates for signing RAUC bundles, with each certificate tied to a unique ephemeral key.
-
Use RAUC Bundle Signing:
- The ephemeral key is generated in the HSM, used for signing, and then discarded to enhance security.
Key Features of RAUC Signing¶
- Ephemeral Keys and Certificates: Each RAUC bundle is signed with a unique, one-time key and certificate, generated on demand and discarded after use.
- HSM Security: All cryptographic operations occur within the HSM's secure boundary, ensuring key protection and integrity.
- Revocation Capability: Using unique certificates for each bundle allows for selective revocation of compromised or outdated bundles.
Best Practices¶
- Use Unique Certificate Names: Opt for a UUID-based Common Name (CN) for certificates to ensure uniqueness and traceability.
- Validate Configuration: Verify that the HSM token and profile are correctly configured to support RAUC signing operations.
- Document Configuration Details: Maintain detailed records of the product and PKI configurations for troubleshooting and auditing purposes.
Next Steps¶
- Review the Product Configuration Guide for detailed instructions on setting up a RAUC signing-enabled product.
- Configure PKI profiles as outlined in RAUC Signing Template Examples.
- Test the RAUC bundle signing process to ensure proper key and certificate generation.
Input data for RAUC signing¶
The input is a tar.gz file containing a request.json file data directory containing the files to be signed or previously signed package. The request.json is used to inform what kind of payload there is in the tar package and how to process that. Please make sure the format field in the request.json matches the RAUC manifest format field.
Plain bundling and resign cases¶
Plain bundle request.json is as follows
request.json:
{
"format": "plain", <===format of the bundle: plain, verity
"directory": "data", <===directory inside the tar package which contains the data to be signed/encrypted
"mkSquashfsArgs": "-b 64k" <===Optional params to mksquash. Remove entry if not needed. Used when creating the bundle
}
Example of plain payload
$ tar -tvf rauc-plain.tar.gz
drwxrwxr-x user/user 0 2025-03-27 14:34 data/
-rw-rw-r-- user/user 65536 2025-03-27 14:10 data/rootfs.img
-rw-rw-r-- user/user 32768 2025-03-27 14:10 data/appfs.img
-rw-rw-r-- user/user 8192 2025-03-27 14:10 data/space-dummy
-rw-rw-r-- user/user 144 2025-03-27 15:39 data/manifest.raucm
-rw-rw-r-- user/user 52 2025-03-27 14:15 request.json
Plain resign request.json is as follows:
request.json:
{
"format": "plain", <===format of the bundle: plain, verity
"resign": true, <===set to true if resign the package. Then input file must be provided
"inputFile": "update.raucb" <=== previously signed package
}
Example of plain resign payload
$ tar -tvf rauc-plain-resign.tar.gz
drwxrwxr-x vagrant/vagrant 0 2025-05-22 14:52 ./
-rw-rw-r-- vagrant/vagrant 13763 2025-05-22 13:41 ./update.raucb
-rw-rw-r-- vagrant/vagrant 78 2025-05-22 14:52 ./request.json
Verity bundling and resign cases¶
verity bundle request.json is as follows
request.json:
{
"format": "verity", <===format of the bundle: plain, verity
"directory": "data", <===directory inside the tar package which contains the data to be signed/encrypted
"mkSquashfsArgs": "-b 64k" <===Optional params to mksquash. Remove entry is not needed. Used when creating the bundle
}
Example of verity payload
$ tar -tvf rauc-verity.tar.gz
drwxrwxr-x vagrant/vagrant 0 2025-03-27 14:34 data/
-rw-rw-r-- vagrant/vagrant 65536 2025-03-27 14:10 data/rootfs.img
-rw-rw-r-- vagrant/vagrant 32768 2025-03-27 14:10 data/appfs.img
-rw-rw-r-- vagrant/vagrant 8192 2025-03-27 14:10 data/space-dummy
-rw-rw-r-- vagrant/vagrant 144 2025-03-27 15:39 data/manifest.raucm
-rw-rw-r-- vagrant/vagrant 52 2025-03-27 14:15 request.json
verity resign request.json is as follows:
request.json:
{
"format": "verity", <===format of the bundle: plain, verity
"resign": true, <===set to true if resign the package. Then input file must be provided
"inputFile": "update.raucb" <=== previously signed package
}
Example of verity resign payload
$ tar -tvf rauc-verity-resign.tar.gz
drwxrwxr-x vagrant/vagrant 0 2025-05-22 14:52 ./
-rw-rw-r-- vagrant/vagrant 18350 2025-05-22 14:52 ./update.raucb
-rw-rw-r-- vagrant/vagrant 80 2025-05-22 14:53 ./request.json
Crypt bundling¶
crypt bundle request.json is as follows
request.json:
{
"format": "crypt", <===format of the bundle: plain, verity or crypt
"directory": "data", <===directory inside the tar package which contains the data to be signed/encrypted
"encryptionCertsFile": "certs.pem" <===file containing a list of recipient certificates in PEM format. Only used if format is crypt.
}
For crypt bundle the manifest.raucm needs to have the bundle format set to crypt. An example of manifest.raucm
[update]
compatible=Test Config
version=2011.03-2
[bundle]
format=crypt
[image.rootfs]
filename=rootfs.img
[image.appfs]
filename=appfs.img
Example of crypt payload
drwxrwxr-x vagrant/vagrant 0 2025-05-19 12:51 ./
-rw-rw-r-- vagrant/vagrant 89 2025-03-27 14:16 ./request.json
drwxrwxr-x vagrant/vagrant 0 2025-03-27 14:35 ./data/
-rw-rw-r-- vagrant/vagrant 143 2025-03-27 15:39 ./data/manifest.raucm
-rw-rw-r-- vagrant/vagrant 65536 2025-03-21 08:48 ./data/rootfs.img
-rw-rw-r-- vagrant/vagrant 32768 2025-03-21 08:48 ./data/appfs.img
-rw-rw-r-- vagrant/vagrant 8192 2025-03-21 08:48 ./data/space-dummy
-rw-rw-r-- vagrant/vagrant 1424 2025-07-21 11:42 ./test.pem
Crypt resign is not supported.
Output data¶
The output download is already in binary format. When verifying the system.conf must have a check-purpose=codesign entry in the keyring section. See example below.
Example response when checking with rauc tool the package¶
The signature is not verified:
$ rauc info -c testdata/system.conf --no-verify /tmp/tmpdownload.bin
rauc-Message: 11:50:09.604: Using central status file /tmp/rauc/central.raucs
rauc-Message: 11:50:09.605: Failed to load system status: No such file or directory
rauc-Message: 11:50:09.605: Reading bundle: /tmp/tmpdownload.bin
Compatible: 'Test Config'
Version: '2011.03-2'
Description: '(null)'
Build: '(null)'
Hooks: ''
Bundle Format: verity
Verity Salt: 'f7dcb7e174024a6e0f8e270e4135966343e1eacd27036272039e93ce054d2f1f'
Verity Hash: '5cb373656d5a2d931d5834dff0009287b59c9bb0114e0ca4422301bf18715c9f'
Verity Size: 4096
Manifest Hash: 'f2b9334db73c0f5ef6dcfff5ceaa32e58be6801a290b12569450d6823fb5abe8'
2 Images:
[rootfs]
Filename: rootfs.img
Checksum: de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31
Size: 65.5 kB (65,536 bytes)
Hooks:
[appfs]
Filename: appfs.img
Checksum: c35020473aed1b4642cd726cad727b63fff2824ad68cedd7ffb73c7cbd890479
Size: 32.8 kB (32,768 bytes)
Hooks:
Signature unverified
Example response with validation¶
The CA certificate used for verification can be obtained using the client functionality
(venv) $ ./signing-tool.py -c -t $TOKEN -a https://app.laavat.io/<CustomerName>/api/v1/ \
secrets get -P 53a0a1da-3af9-4279-a62a-687d3b4f5c9b \
-C client.private -O /tmp/prod.json
Full secret payload written to: /tmp/prod.json
Then extract the CA certificate chain
$ jq -r .caInfo[0].pkcs7chain < /tmp/prod.json | base64 -d \
| openssl pkcs7 -print_certs > /tmp/cachain.pem
Check the signature:
$ rauc info -c testdata/system.conf \
--keyring=/tmp/cachain.pem /tmp/signingresponse.bin
rauc-Message: 12:06:36.026: Using central status file /tmp/rauc/central.raucs
rauc-Message: 12:06:36.026: Failed to load system status: No such file or directory
rauc-Message: 12:06:36.026: Reading bundle: /tmp/rauctest.bin
rauc-Message: 12:06:36.027: Verifying bundle signature...
rauc-Message: 12:06:36.028: Verified inline signature by 'C = FI, O = Test Comp, CN = 3adbce62-ded3-496c-bcb0-950bf0343915'
Compatible: 'Test Config'
Version: '2011.03-2'
Description: '(null)'
Build: '(null)'
Hooks: ''
Bundle Format: verity
Verity Salt: 'd759d02e534409c0eb935eb7a18cea501f5d353635aa376fbc596a2f49c7c4cb'
Verity Hash: 'b8a2e2ac03c822b1762acbc6bf8cc717b19d71708d9b532959bf72e121917df4'
Verity Size: 4096
Manifest Hash: '4b018c8c8aa579afcc4bc577f97f407ba09585618b397101cdefa72a510f812c'
2 Images:
[rootfs]
Filename: rootfs.img
Checksum: de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31
Size: 65.5 kB (65,536 bytes)
Hooks:
[appfs]
Filename: appfs.img
Checksum: c35020473aed1b4642cd726cad727b63fff2824ad68cedd7ffb73c7cbd890479
Size: 32.8 kB (32,768 bytes)
Hooks:
Certificate Chain:
0 Subject: C = FI, O = Test Comp, CN = 3adbce62-ded3-496c-bcb0-950bf0343915
Issuer: C = FI, CN = CA for One Time Signing certs
SPKI sha256: CF:53:B1:1D:0B:6B:61:FA:CB:DA:AF:92:5E:A0:03:84:BB:A8:EA:28:52:AE:83:78:96:6C:4F:5F:E5:59:C3:C9
Not Before: Sep 30 08:52:37 2025 GMT
Not After: Aug 13 08:40:57 2225 GMT
1 Subject: C = FI, CN = CA for One Time Signing certs
Issuer: C = FI, CN = CA for One Time Signing certs
SPKI sha256: 92:78:D8:02:B2:DB:E7:B9:D8:63:11:73:99:C2:DF:41:8B:7D:2D:2B:10:95:C7:16:2D:1C:F9:F8:E2:9F:1D:20
Not Before: Sep 30 08:40:57 2025 GMT
Not After: Aug 13 08:40:57 2225 GMT
The system.conf used in the testing.
Make sure the keyring section contains the check-purpose variable.
[system]
compatible=FooCorp Super BarBazzer
bootloader=barebox
data-directory=/tmp/rauc
bundle-formats=-plain
[keyring]
check-purpose=codesign
Example response for encrypted bundles with validation¶
The CA certificate used for verification can be obtained using the client functionality
For example:
(venv) $ ./signing-tool.py -c -t $TOKEN -a https://app.laavat.io/<CustomerName>/api/v1/ \
secrets get -P 53a0a1da-3af9-4279-a62a-687d3b4f5c9b \
-C client.private -O /tmp/prod.json
Full secret payload written to: /tmp/prod.json
Then extract the CA certificate chain
$ jq -r .caInfo[0].pkcs7chain < /tmp/prod.json | base64 -d \
| openssl pkcs7 -print_certs > /tmp/cachain.pem
private.pem contains the private key of one of the recipients. The public certificates were delivered as part of signingrequest. See encryptionCertsFile option in the request.json.
Decrypt and check the signature
$ rauc info -c "testdata/system.conf" /tmp/tmpdownload.bin \
--key=private.pem --keyring /tmp/cachain.pem
rauc-Message: 12:10:39.607: Using central status file /tmp/rauc/central.raucs
rauc-Message: 12:10:39.607: Failed to load system status: No such file or directory
rauc-Message: 12:10:39.607: Reading bundle: /tmp/rauctestenc.bin
rauc-Message: 12:10:39.611: Decrypting signature...
rauc-Message: 12:10:39.612: Verifying bundle signature...
rauc-Message: 12:10:39.613: Verified inline signature by 'C = FI, O = Test Comp, CN = f48fa4d2-ec87-45dd-abb0-df4fba62344a'
Compatible: 'Test Config'
Version: '2011.03-2'
Description: '(null)'
Build: '(null)'
Hooks: ''
Bundle Format: crypt [encrypted CMS]
Crypt Key: '<hidden>'
Verity Salt: '9859076f5f9699e29e08293d4fe9ab5c4ecf6eb8860bafe0fc1ec6e0530d6d76'
Verity Hash: 'fc35e33cfa5008354cd747f7c2815da9f7b5fe7311fb3bef06ce0d12ecf28295'
Verity Size: 4096
Manifest Hash: '22c7a5dd4a5679f08b06318be7eeeb06b3cd3d57b1a2cbc4b9ecb298aae3b34a'
2 Images:
[rootfs]
Filename: rootfs.img
Checksum: de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31
Size: 65.5 kB (65,536 bytes)
Hooks:
[appfs]
Filename: appfs.img
Checksum: c35020473aed1b4642cd726cad727b63fff2824ad68cedd7ffb73c7cbd890479
Size: 32.8 kB (32,768 bytes)
Hooks:
Certificate Chain:
0 Subject: C = FI, O = Test Comp, CN = f48fa4d2-ec87-45dd-abb0-df4fba62344a
Issuer: C = FI, CN = CA for One Time Signing certs
SPKI sha256: 74:2C:FE:17:DB:43:1B:5F:B1:26:71:8E:D7:13:7C:C2:74:E7:74:E5:61:3C:44:01:90:DC:FE:23:61:A4:3C:93
Not Before: Sep 30 09:08:18 2025 GMT
Not After: Aug 13 08:40:57 2225 GMT
1 Subject: C = FI, CN = CA for One Time Signing certs
Issuer: C = FI, CN = CA for One Time Signing certs
SPKI sha256: 92:78:D8:02:B2:DB:E7:B9:D8:63:11:73:99:C2:DF:41:8B:7D:2D:2B:10:95:C7:16:2D:1C:F9:F8:E2:9F:1D:20
Not Before: Sep 30 08:40:57 2025 GMT
Not After: Aug 13 08:40:57 2225 GMT
Make sure the keyring contains the check-purpose variable
[system]
compatible=FooCorp Super BarBazzer
bootloader=barebox
data-directory=/tmp/rauc
bundle-formats=-plain
[keyring]
check-purpose=codesign
Example usage with reference client package: RAUC signing¶
RAUC signing examples ($TOKEN contains the "regular" user token and $APPROVERTOKEN contains a token for a user that's in the approvers group):
(venv) $./signing-tool.py -c -t $TOKEN -a https://app.laavat.io/<CustomerName>/api/v1 imagesigning add RaucBundleSigning \
-N test -D test2 -P adcb30d8-f009-438e-b1b2-96f507b306cb \
--operid 4add90e9-ffb3-4708-9554-ed2e82e8fd71 -F rauc-verity.tar.gz
# Approve the rauc signing request
(venv) $ ./signing-tool.py -c -t $APPROVERTOKEN -a https://app.laavat.io/<CustomerName>/api/v1 imagesigning \
approve -I a83081a6-1d3b-4117-a81b-0ebcfcf0669c
# Get the signed payload
(venv) $ ./signing-tool.py -c -t $TOKEN -a https://app.laavat.io/<CustomerName>/api/v1 imagesigning \
get -I a83081a6-1d3b-4117-a81b-0ebcfcf0669c -O /tmp/signed.rauc.bundle