> Markdown version of https://docs.laavat.io/usage/signing-encryption/signing-encryption/ from the LAAVAT PKI and Signing Platform documentation. All pages: https://docs.laavat.io/llms.txt

# Signing and Encryption

When the product is created, there are one or many different operations which can be performed. E.g. RAUC signing, signing of digest or FIT signing.

Below is the generic process for imagesigning:

* First, the AWS S3 presigned URL is obtained from the Solution.
* Using the URL the payload is uploaded to the AWS S3 bucket.
* S3 provides an ETag in successful upload.
* The imagesigning request with relevant info, productid, and product operationid and the ETag is created to the Solution.
* Depending on the Approval rule, the request might require approval
* Once approved, the request is processed.
* Client is polling the request until the state is either success or failure
* In a successful case, the AWS S3 presigned URL is provided, and the result can be downloaded.

This upload/download process applies to content of up to about 3.75 GiB: the
content is uploaded as Base64 text through a single presigned URL, and S3
accepts at most 5 GiB in a single upload. Individual operations may still impose
their own limits — notably the AES-GCM modes, which encrypt in a single pass:
4 GiB for the `AES-GCM-*-PSKEK` modes and for software-protected keys, and
16 248 bytes, shared with the AAD, for a plain `AES-GCM-*` mode with an
HSM-backed key. See
[AES image encryption](https://docs.laavat.io/usage/signing-encryption/aes-encryption/#gcm-modes-aes-gcm-aes-gcm-pskek).

Operations that take an archive unpack it within these limits; an archive over
a limit fails the request:

| Operation | Upload, decoded | Unpacked in total | Per file | Entries |
| --- | --- | --- | --- | --- |
| RAUC, Xilinx Bootgen, JAR, Windows | 16 GiB | 32 GiB | 8 GiB | 20,000 |
| OP-TEE, MCUboot | 256 MiB | 32 GiB | 512 MiB | 2,000 |
| SPSDK | 512 MiB | 1 GiB | 512 MiB | 200 |
| CST (archive mode) | - | 2 GiB | 1 GiB | 64 |

The 3.75 GiB upload limit applies first, so the 16 GiB limit is never reached.

### Digest signing

Add a digest for signing. The digest needs to be b64 encoded raw binary digest. Approve if needed, then
download the signature with `imagesigning get -I <request-id> --wait -O <file>`; it is decoded into binary form.
`-O` on `add` only prints that `get` command. Hash algorithms, RSA-PSS, raw ECDSA, CMS settings and
envelopes are described in [Digest signing](https://docs.laavat.io/usage/signing-encryption/digest-signing/).

```bash
# you can get the suitable form of the digest with the following
sha256sum myfile.bin | cut -d" " -f1 | xxd -r -p | base64

# Send the request
(venv) $ signing-tool -n test.ini imagesigning add DigestSigning \
    -P b508d600-977a-46f4-9070-5cf56646bae1 --operid f7a29d80-7df4-4546-bea7-201d199acb1d \
    -p "NA+2NhI/XlnojS1iY8yoo6+9YdOu1Fo6BnGk6KLZCuo=" -H SHA256
{
    "call_back_url": null,
    "description": "digest signing request",
    "id": "40297ad0-5ba1-4d9c-b262-3591fcc6ac46",
    "id_product": "b508d600-977a-46f4-9070-5cf56646bae1",
    "id_product_operation": "f7a29d80-7df4-4546-bea7-201d199acb1d",
    "name": "Digest signing request",
    "payload": {
        "id": null,
        "metadata": [
            {
                "name": "Version",
                "value": "1.0"
            }
        ],
        "name": "Signing payload",
        "s3_url": null,
        "service_provided_parameters": null
    },
    "state": 1
}
Image signing request sent. Request ID: 40297ad0-5ba1-4d9c-b262-3591fcc6ac46 state: Created
(venv) $ 
```

Should you need to retrieve the signature again from a given request. You can do the following:

```bash
(venv) $ signing-tool -n test.ini imagesigning get \
    -I 40297ad0-5ba1-4d9c-b262-3591fcc6ac46 -O signed.bin
{
    "call_back_url": null,
    "description": "digest signing request",
    "id": "40297ad0-5ba1-4d9c-b262-3591fcc6ac46",
    "id_product": "b508d600-977a-46f4-9070-5cf56646bae1",
    "id_product_operation": "f7a29d80-7df4-4546-bea7-201d199acb1d",
    "name": "Digest signing request",
    "payload": {
        "id": "336d0c78-59f0-44c5-95d5-e2e6b3c07b7a",
        "metadata": [
            {
                "name": "Version",
                "value": "1.0"
            }
        ],
        "name": "Signing payload",
        "s3_url": "<redacted>",
        "service_provided_parameters": []
    },
    "state": 16
}
Downloading signed binary to: signed.bin

File Downloaded
(venv) $
```

### Signing with ML-DSA keys

A `DigestSigning` operation whose token is an ML-DSA key (`MLDSA44`, `MLDSA65` or `MLDSA87`) signs the **message itself**.
ML-DSA (FIPS 204) has no separate hashing step, so the request differs from RSA and EC digest signing in three ways:

- The payload is the base64-encoded message, not a digest.
- The hash algorithm is `NONE` and the signature padding is `NONE`.
- The result is the raw FIPS 204 signature over the message (2420, 3309 or 4627 bytes). A CMS (`--format CMS -H SHA512`,
  RFC 9882) signs a SHA-512 digest instead; see [Digest signing](https://docs.laavat.io/usage/signing-encryption/digest-signing/).

With the reference client, give the base64 of the message with `-p` and `-H NONE`; the client then sends hash algorithm
`NONE` and signature padding `NONE`:

```bash
(venv) $ signing-tool -n test.ini imagesigning add DigestSigning \
    -P b508d600-977a-46f4-9070-5cf56646bae1 --operid f7a29d80-7df4-4546-bea7-201d199acb1d \
    -p "$(base64 -w0 message.bin)" -H NONE -O message.sig
```

With the REST API, upload the base64-encoded message as the payload and set the optional parameters of the image signing
request:

```json
"optionalParameters": [
    { "hashAlgorithm": "NONE" },
    { "SignaturePadding": "NONE" }
]
```

A digest sent with `SHA256` or `SHA512` to an ML-DSA key is refused, and so is `NONE` for an RSA or EC key.

> **Info: Message size**
> The HSM signs the message in one call, and the platform's HSM accepts at most **13504 bytes**. A larger message makes the
> request fail. To sign larger data, sign a digest of it as the message and have the verifier compute the same digest first.

The public key is the `publicKey` of the operation's token in the product. Verify the signature with any FIPS 204
implementation, for example OpenSSL 3.5 or later:

```bash
openssl pkeyutl -verify -pubin -inkey mldsa.pub.pem -rawin -in message.bin -sigfile message.sig
```

### (A)HAB signing using detached digest

Similar to digest signing, you may use the -O flag here to have the client automatically download the result if no approval is required.

CSF signing:

```bash
(venv) $ signing-tool -n test.ini imagesigning add SignHABCSF \
    -P b508d600-977a-46f4-9070-5cf56646bae1 --operid f7ae0503-5a6d-47b6-bfc2-6decc5557dec \
    -H SHA256 -p "NA+2NhI/XlnojS1iY8yoo6+9YdOu1Fo6BnGk6KLZCuo=" -O habout.test
{
    "call_back_url": null,
    "description": "digest signing request",
    "id": "7a61bf36-4776-4bdb-a9ba-dfe72fa4b871",
    "id_product": "b508d600-977a-46f4-9070-5cf56646bae1",
    "id_product_operation": "f7ae0503-5a6d-47b6-bfc2-6decc5557dec",
    "name": "automated request",
    "payload": {
        "id": null,
        "metadata": [
            {
                "name": "Version",
                "value": "1.0"
            }
        ],
        "name": "Signing payload",
        "s3_url": null,
        "service_provided_parameters": null
    },
    "state": 1
}
Image signing request sent. Request ID: 7a61bf36-4776-4bdb-a9ba-dfe72fa4b871, state: Created
Polling for request

File Downloaded
(venv) $
```

IMG signing:

```bash
(venv) $ signing-tool -n test.ini imagesigning add SignHABIMG \
    -P b508d600-977a-46f4-9070-5cf56646bae1 --operid 1c3c9edd-a71a-42f2-8b76-04ce59b355c3 \
    -H SHA256 -p "NA+2NhI/XlnojS1iY8yoo6+9YdOu1Fo6BnGk6KLZCuo=" -O habout.test
{
    "call_back_url": null,
    "description": "digest signing request",
    "id": "76466f78-c3a8-4c77-8876-3c5e4e9fadf1",
    "id_product": "b508d600-977a-46f4-9070-5cf56646bae1",
    "id_product_operation": "1c3c9edd-a71a-42f2-8b76-04ce59b355c3",
    "name": "automated request",
    "payload": {
        "id": null,
        "metadata": [
            {
                "name": "Version",
                "value": "1.0"
            }
        ],
        "name": "Signing payload",
        "s3_url": null,
        "service_provided_parameters": null
    },
    "state": 1
}
Image signing request sent. Request ID: 76466f78-c3a8-4c77-8876-3c5e4e9fadf1, state: Created
Polling for request
File Downloaded
```

SGK signing:

```bash
(venv) $ signing-tool -n test.ini imagesigning add SignAHAB \
    -P b508d600-977a-46f4-9070-5cf56646bae1 --operid 1c3c9edd-a71a-42f2-8b76-04ce59b355c3 \
    -H SHA256 -p "NA+2NhI/XlnojS1iY8yoo6+9YdOu1Fo6BnGk6KLZCuo=" -O habout.test
{
    "call_back_url": null,
    "description": "digest signing request",
    "id": "76466f78-c3a8-4c77-8876-3c5e4e9fadf1",
    "id_product": "b508d600-977a-46f4-9070-5cf56646bae1",
    "id_product_operation": "1c3c9edd-a71a-42f2-8b76-04ce59b355c3",
    "name": "automated request",
    "payload": {
        "id": null,
        "metadata": [
            {
                "name": "Version",
                "value": "1.0"
            }
        ],
        "name": "Signing payload",
        "s3_url": null,
        "service_provided_parameters": null
    },
    "state": 1
}
Image signing request sent. Request ID: 76466f78-c3a8-4c77-8876-3c5e4e9fadf1, state: Created
Polling for request
File Downloaded
```

Please refer to the digest signing section should you need to retrieve the output of a request again.

### SPSDK AHAB signing

AHAB boot containers for i.MX8QXP and i.MX9 devices are signed with the
`SignSPSDK` operation, which runs NXP's Secure Provisioning SDK. Instead of a
single binary, the request carries a small compressed archive (the image, a
short SPSDK configuration file, and a `request.json` that names them). Both
elliptic-curve (EC) and RSA keys are supported. See
[SPSDK signing](https://docs.laavat.io/usage/signing-encryption/spsdksigning/) for the full instructions and examples.

Note that `SignAHAB` above is a different operation: it signs only a hash
(digest) with the AHAB SGK key, not a whole image.
