Skip to content

Commit

Permalink
Merge pull request wolfSSL#412 from danielinux/custom-tlv
Browse files Browse the repository at this point in the history
Added support for custom TLVs in manifest header
  • Loading branch information
dgarske authored Feb 29, 2024
2 parents 69556cd + 0996e0f commit dff83a8
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 2 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/test-keytools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,30 @@ jobs:
run: |
./tools/keytools/keygen --id 1,3,5,10,11,13,14 --ecc256 -g wolfboot_signing_private_key.der | grep "mask" | grep "00006c2a"
# Custom TLVs
- name: make clean
run: |
make distclean
- name: Select config
run: |
cp config/examples/sim.config .config && make include/target.h
- name: Build tools
run: |
make -C tools/keytools && make -C tools/bin-assemble
- name: Build wolfboot with ECC256/SHA256
run: |
make SIGN=ECC256 HASH=SHA256
- name: Sign app with custom numeric TLV included
run: |
./tools/keytools/sign --ecc256 --sha256 --custom-tlv 0x45 4 0x6f616943 test-app/image.elf wolfboot_signing_private_key.der 2
grep "Ciao" test-app/image_v2_signed.bin
- name: Sign app with custom buffer TLV included
run: |
./tools/keytools/sign --ecc256 --sha256 --custom-tlv-buffer 0x46 48656C6C6F20776F726C64 test-app/image.elf wolfboot_signing_private_key.der 3
grep "Hello world" test-app/image_v3_signed.bin
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ For more detailed information about firmware update implementation, see [Firmwar
### Additional features
- [Remote external flash interface](docs/remote_flash.md)
- [External encrypted partitions](docs/encrypted_partitions.md)
- [Delta updates](docs/firmware_update.md#incremental-updates-aka-delta-updates)

## Building

Expand Down
17 changes: 17 additions & 0 deletions docs/Signing.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,23 @@ Provides a PCR mask and digest to be signed and included in the header. The sign
A copy of the final signed policy (including 4 byte PCR mask) will be output to `[inputname].sig`.
Note: This may require increasing the `IMAGE_HEADER_SIZE` as two signatures will be stored in the header.

#### Adding custom fields to the manifest header

Provides a value to be set with a custom tag

* `--custom-tlv tag len val`: Adds a TLV entry to the manifest header, corresponding
to the type identified by `tag`, with lenght `len` bytes, and assigns the value `val`.
Values can be decimal or hex numbers (prefixed by '0x'). The tag is a 16-bit number.
Valid tags are in the range between 0x0030 and 0xFEFE.

* `--custom-tlv-buffer tag value`: Adds a TLV entry with arbitrary length to the manifest
header, corresponding to the type identified by `tag`, and assigns the value `value`. The
tag is a 16-bit number. Valid tags are in the range between 0x0030 and 0xFEFE. The length
is implicit, and is the length of the value.
Value argument is in the form of a hex string, e.g. `--custom-tlv-buffer 0x0030 AABBCCDDEE`
will add a TLV entry with tag 0x0030, length 5 and value 0xAABBCCDDEE.


#### Three-steps signing using external provisioning tools

If the private key is not accessible, while it's possible to sign payloads using
Expand Down
42 changes: 40 additions & 2 deletions docs/firmware_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,53 @@ Each **Type** has a different meaning, and integrate information about the firmw

- A 'version' Tag (type: 0x0001, size: 4 Bytes) indicating the version number for the firmware stored in the image
- A 'timestamp' Tag (type: 0x0002, size 8 Bytes) indicating the timestamp in unix seconds for the creation of the firmware
- A 'sha256 digest' Tag (type: 0x0003, size: 32 Bytes) used for integrity check of the firmware
- A 'sha digest' Tag (type: 0x0003, size: digest size (32 Bytes for SHA256)) used for integrity check of the firmware
- A 'firmware signature' Tag (type: 0x0020, size: 64 Bytes) used to validate the signature stored with the firmware against a known public key
- A 'firmware type' Tag (type: 0x0030, size: 2 Bytes) used to identify the type of firmware, and the authentication mechanism in use.

Optionally, a 'public key hint digest' Tag can be transmitted in the header (type: 0x10, size:32 Bytes). This Tag contains the SHA256 digest of the public key used
A 'public key hint digest' tag is transmitted in the header (type: 0x10, size:32 Bytes). This tag contains the SHA digest of the public key used
by the signing tool. The bootloader may use this field to locate the correct public key in case of multiple keys available.

wolfBoot will, in all cases, refuse to boot an image that cannot be verified and authenticated using the built-in digital signature authentication mechanism.

### Adding custom fields to the manifest header

It is possible to add custom fields to the manifest header, by using the `--custom-tlv` option in the signing tool.

In order for the fields to be secured (checked by wolfBoot for integrity and authenticity),
their value is placed in the manifest header before the signature is calculated. The signing tool takes care of the alignment and padding of the fields.

The custom fields are identified by a 16-bit tag, and their size is indicated by a 16-bit length field. The tag and length fields are stored in little-endian format.

At runtime, the values stored in the manifest header can be accessed using the `wolfBoot_find_header` function.

The syntax for `--custom-tlv` option is also documented in [docs/Signing.md](/docs/Signing.md#adding-custom-fields-to-the-manifest-header).

### Image header: Example

This example adds a custom field when the signing tool is used to sign the firmware image:

```bash
./tools/keytools/sign --ed25519 --custom-tlv 0x34 4 0xAABBCCDD test-app/image.bin wolfboot_signing_private_key.der 4
```

The output image `test-app/image_v4_signed.bin` will contain the custom field with tag `0x34` with length `4` and value `0xAABBCCDD`.

From the bootloader code, we can then retrieve the value of the custom field using the `wolfBoot_find_header` function:

```c
uint32_t custom_code34_field;
const uint16_t custom_code34_field_size = 4;
const uint16_t custom_code34_tag = 0x34;
int size;

size = wolfBoot_find_header(0x34, &custom_code34_field, sizeof(custom_code34_value));
if (size != custom_code34_field_size) {
/* Error: the field is not present or has the wrong size */
}

/* From here, the value 0xAABBCCDD is stored in custom_code34_value */
```

### Image signing tool

Expand Down
155 changes: 155 additions & 0 deletions tools/keytools/sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <sys/types.h>
#include <fcntl.h>
#include <stddef.h>
#include <inttypes.h>
/* target.h is a generated file based on .config (see target.h.in)
* Provides: WOLFBOOT_SECTOR_SIZE */
#include <target.h>
Expand Down Expand Up @@ -68,6 +69,8 @@ static inline int fp_truncate(FILE *f, size_t len)

#define MAX_SRC_SIZE (1 << 24)

#define MAX_CUSTOM_TLVS (16)

#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/aes.h>
Expand Down Expand Up @@ -265,6 +268,13 @@ struct cmd_options {
uint32_t signature_sz;
uint32_t policy_sz;
uint8_t partition_id;
uint32_t custom_tlvs;
struct cmd_tlv {
uint16_t tag;
uint16_t len;
uint64_t val;
uint8_t *buffer;
} custom_tlv[MAX_CUSTOM_TLVS];
};

static struct cmd_options CMD = {
Expand All @@ -273,6 +283,7 @@ static struct cmd_options CMD = {
.hash_algo = HASH_SHA256,
.header_sz = IMAGE_HEADER_SIZE,
.partition_id = HDR_IMG_TYPE_APP

};

static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
Expand Down Expand Up @@ -1067,6 +1078,28 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
&patch_inv_len);
}

/* Add custom TLVs */
if (CMD.custom_tlvs > 0) {
uint32_t i;
for (i = 0; i < CMD.custom_tlvs; i++) {
if (CMD.custom_tlv[i].len == 8) {
/* This field requires 8-byte alignment */
while((header_idx % 8) != 4)
header_idx++;
}
if (CMD.custom_tlv[i].buffer == NULL) {
header_append_tag(header, &header_idx, CMD.custom_tlv[i].tag,
CMD.custom_tlv[i].len, &CMD.custom_tlv[i].val);
} else {
header_append_tag(header, &header_idx, CMD.custom_tlv[i].tag,
CMD.custom_tlv[i].len, CMD.custom_tlv[i].buffer);
}
}
/* Align for next field */
while ((header_idx % 4) != 0)
header_idx++;
}

/* Add padding bytes. Sha-3 val field requires 8-byte alignment */
while ((header_idx % 8) != 4)
header_idx++;
Expand Down Expand Up @@ -1789,6 +1822,31 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
return ret;
}

uint64_t arg2num(const char *arg, size_t len)
{
uint64_t ret = (uint64_t) -1;
if (strncmp(arg, "0x", 2) == 0) {
ret = strtoll(arg + 2, NULL, 16);
} else {
ret = strtoll(arg, NULL, 10);
}
switch (len) {
case 1:
ret &= 0xFF;
break;
case 2:
ret &= 0xFFFF;
break;
case 4:
ret &= 0xFFFFFFFF;
case 8:
break;
default:
ret = (uint64_t) (-1);
}
return ret;
}

int main(int argc, char** argv)
{
int ret = 0;
Expand Down Expand Up @@ -1936,6 +1994,81 @@ int main(int argc, char** argv)
CMD.policy_sign = 1;
CMD.policy_file = argv[++i];
}
else if (strcmp(argv[i], "--custom-tlv") == 0) {
int p = CMD.custom_tlvs;
uint16_t tag, len;
if (p >= MAX_CUSTOM_TLVS) {
fprintf(stderr, "Too many custom TLVs.\n");
exit(16);
}
if (argc < (i + 3)) {
fprintf(stderr, "Invalid custom TLV fields. \n");
exit(16);
}
tag = (uint16_t)arg2num(argv[i + 1], 2);
len = (uint16_t)arg2num(argv[i + 2], 2);

if (tag < 0x0030) {
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
exit(16);
}
if ( ((tag & 0xFF00) == 0xFF00) || ((tag & 0xFF) == 0xFF) ) {
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
exit(16);
}

if ((len != 1) && (len != 2) && (len != 4) && (len != 8)) {
fprintf(stderr, "Invalid custom tag len: %s\n", argv[i + 2]);
fprintf(stderr, "Accepted len: 1, 2, 4 or 8\n");
exit(16);
}

CMD.custom_tlv[p].tag = tag;
CMD.custom_tlv[p].len = len;
CMD.custom_tlv[p].val = arg2num(argv[i+3], len);
CMD.custom_tlv[p].buffer = NULL;
CMD.custom_tlvs++;
i += 3;
} else if (strcmp(argv[i], "--custom-tlv-buffer") == 0) {
int p = CMD.custom_tlvs;
uint16_t tag, len;
uint32_t j;
if (p >= MAX_CUSTOM_TLVS) {
fprintf(stderr, "Too many custom TLVs.\n");
exit(16);
}
if (argc < (i + 2)) {
fprintf(stderr, "Invalid custom TLV fields. \n");
exit(16);
}
tag = (uint16_t)arg2num(argv[i + 1], 2);
len = (uint16_t)strlen(argv[i + 2]) / 2;
if (tag < 0x0030) {
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
exit(16);
}
if ( ((tag & 0xFF00) == 0xFF00) || ((tag & 0xFF) == 0xFF) ) {
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
exit(16);
}
if (len > 255) {
fprintf(stderr, "custom tlv buffer size too big: %s\n", argv[i + 2]);
exit(16);
}
CMD.custom_tlv[p].tag = tag;
CMD.custom_tlv[p].len = len;
CMD.custom_tlv[p].buffer = malloc(len);
if (CMD.custom_tlv[p].buffer == NULL) {
fprintf(stderr, "Error malloc for custom tlv buffer %d\n", len);
exit(16);
}
for (j = 0; j < len; j++) {
char c[3] = {argv[i + 2][j * 2], argv[i + 2][j * 2 + 1], 0};
CMD.custom_tlv[p].buffer[j] = (uint8_t)strtol(c, NULL, 16);
}
CMD.custom_tlvs++;
i += 2;
}
else {
i--;
break;
Expand Down Expand Up @@ -2010,6 +2143,28 @@ int main(int argc, char** argv)
printf("(bootloader)");
printf("\n");

if (CMD.custom_tlvs > 0) {
uint32_t i, j;
printf("Custom TLVS: %u\n", CMD.custom_tlvs);
for (i = 0; i < CMD.custom_tlvs; i++) {
printf("TLV %u\n", i);
printf("----\n");
if (CMD.custom_tlv[i].buffer) {
printf("Tag: %04X Len: %hu Val: ", CMD.custom_tlv[i].tag,
CMD.custom_tlv[i].len);
for (j = 0; j < CMD.custom_tlv[i].len; j++) {
printf("%02X", CMD.custom_tlv[i].buffer[j]);
}
printf("\n");

} else {
printf("Tag: %04X Len: %hu Val: %" PRIu64 "\n", CMD.custom_tlv[i].tag,
CMD.custom_tlv[i].len, CMD.custom_tlv[i].val);
}
printf("-----\n");
}
}

/* get header and signature sizes */
if (CMD.sign == SIGN_ED25519) {
if (CMD.header_sz < 256)
Expand Down

0 comments on commit dff83a8

Please sign in to comment.