Skip to content

Commit 43f7730

Browse files
committed
Added support for custom TLVs in manifest header
1 parent c2388cd commit 43f7730

4 files changed

Lines changed: 146 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ For more detailed information about firmware update implementation, see [Firmwar
9090
### Additional features
9191
- [Remote external flash interface](docs/remote_flash.md)
9292
- [External encrypted partitions](docs/encrypted_partitions.md)
93+
- [Delta updates](docs/firmware_update.md#incremental-updates-aka-delta-updates)
9394

9495
## Building
9596

docs/Signing.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,18 @@ Provides a PCR mask and digest to be signed and included in the header. The sign
177177
A copy of the final signed policy (including 4 byte PCR mask) will be output to `[inputname].sig`.
178178
Note: This may require increasing the `IMAGE_HEADER_SIZE` as two signatures will be stored in the header.
179179

180+
#### Adding custom fields to the manifest header
181+
182+
Provides a value to be set with a custom tag
183+
184+
* `--custom-tlv tag len val`: Adds a TLV entry to the manifest header, corresponding
185+
to the type identified by `tag`, with lenght `len` bytes, and assigns the value `val`.
186+
Values can be decimal or hex numbers (prefixed by '0x'). The tag is a 16-bit number.
187+
Valid tags are in the range between 0x0030 and 0xFEFE.
188+
189+
The extra numeric field (can be 1, 2, 4, or 8 bytes long) is stored in the manifest
190+
header and can be retrieved at runtime by wolfBoot, using `wolfBoot_find_header()`.
191+
180192
#### Three-steps signing using external provisioning tools
181193

182194
If the private key is not accessible, while it's possible to sign payloads using

docs/firmware_image.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,53 @@ Each **Type** has a different meaning, and integrate information about the firmw
4242

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

49-
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
49+
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
5050
by the signing tool. The bootloader may use this field to locate the correct public key in case of multiple keys available.
5151

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

54+
### Adding custom fields to the manifest header
55+
56+
It is possible to add custom fields to the manifest header, by using the `--custom-tlv` option in the signing tool.
57+
58+
In order for the fields to be secured (checked by wolfBoot for integrity and authenticity),
59+
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.
60+
61+
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.
62+
63+
At runtime, the values stored in the manifest header can be accessed using the `wolfBoot_find_header` function.
64+
65+
The syntax for `--custom-tlv` option is also documented in [docs/Signing.md](/docs/Signing.md#adding-custom-fields-to-the-manifest-header).
66+
67+
### Image header: Example
68+
69+
This example adds a custom field when the signing tool is used to sign the firmware image:
70+
71+
```bash
72+
./tools/keytools/sign --ed25519 --custom-tlv 0x34 4 0xAABBCCDD test-app/image.bin wolfboot_signing_private_key.der 4
73+
```
74+
75+
The output image `test-app/image_v4_signed.bin` will contain the custom field with tag `0x34` with length `4` and value `0xAABBCCDD`.
76+
77+
From the bootloader code, we can then retrieve the value of the custom field using the `wolfBoot_find_header` function:
78+
79+
```c
80+
uint32_t custom_code34_field;
81+
const uint16_t custom_code34_field_size = 4;
82+
const uint16_t custom_code34_tag = 0x34;
83+
int size;
84+
85+
size = wolfBoot_find_header(0x34, &custom_code34_field, sizeof(custom_code34_value));
86+
if (size != custom_code34_field_size) {
87+
/* Error: the field is not present or has the wrong size */
88+
}
89+
90+
/* From here, the value 0xAABBCCDD is stored in custom_code34_value */
91+
```
5492

5593
### Image signing tool
5694

tools/keytools/sign.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <sys/types.h>
4242
#include <fcntl.h>
4343
#include <stddef.h>
44+
#include <inttypes.h>
4445
/* target.h is a generated file based on .config (see target.h.in)
4546
* Provides: WOLFBOOT_SECTOR_SIZE */
4647
#include <target.h>
@@ -68,6 +69,8 @@ static inline int fp_truncate(FILE *f, size_t len)
6869

6970
#define MAX_SRC_SIZE (1 << 24)
7071

72+
#define MAX_CUSTOM_TLVS (16)
73+
7174
#include <wolfssl/wolfcrypt/settings.h>
7275
#include <wolfssl/wolfcrypt/asn.h>
7376
#include <wolfssl/wolfcrypt/aes.h>
@@ -265,6 +268,12 @@ struct cmd_options {
265268
uint32_t signature_sz;
266269
uint32_t policy_sz;
267270
uint8_t partition_id;
271+
uint32_t custom_tlvs;
272+
struct cmd_tlv {
273+
uint16_t tag;
274+
uint16_t len;
275+
uint64_t val;
276+
} custom_tlv[MAX_CUSTOM_TLVS];
268277
};
269278

270279
static struct cmd_options CMD = {
@@ -273,6 +282,7 @@ static struct cmd_options CMD = {
273282
.hash_algo = HASH_SHA256,
274283
.header_sz = IMAGE_HEADER_SIZE,
275284
.partition_id = HDR_IMG_TYPE_APP
285+
276286
};
277287

278288
static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
@@ -1067,6 +1077,23 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
10671077
&patch_inv_len);
10681078
}
10691079

1080+
/* Add custom TLVs */
1081+
if (CMD.custom_tlvs > 0) {
1082+
uint32_t i;
1083+
for (i = 0; i < CMD.custom_tlvs; i++) {
1084+
if (CMD.custom_tlv[i].len == 8) {
1085+
/* This field requires 8-byte alignment */
1086+
while((header_idx % 8) != 4)
1087+
header_idx++;
1088+
}
1089+
header_append_tag(header, &header_idx, CMD.custom_tlv[i].tag,
1090+
CMD.custom_tlv[i].len, &CMD.custom_tlv[i].val);
1091+
}
1092+
/* Align for next field */
1093+
while ((header_idx % 4) != 0)
1094+
header_idx++;
1095+
}
1096+
10701097
/* Add padding bytes. Sha-3 val field requires 8-byte alignment */
10711098
while ((header_idx % 8) != 4)
10721099
header_idx++;
@@ -1789,6 +1816,31 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
17891816
return ret;
17901817
}
17911818

1819+
uint64_t arg2num(const char *arg, size_t len)
1820+
{
1821+
uint64_t ret = (uint64_t) -1;
1822+
if (strncmp(arg, "0x", 2) == 0) {
1823+
ret = strtoll(arg + 2, NULL, 16);
1824+
} else {
1825+
ret = strtoll(arg, NULL, 10);
1826+
}
1827+
switch (len) {
1828+
case 1:
1829+
ret &= 0xFF;
1830+
break;
1831+
case 2:
1832+
ret &= 0xFFFF;
1833+
break;
1834+
case 4:
1835+
ret &= 0xFFFFFFFF;
1836+
case 8:
1837+
break;
1838+
default:
1839+
ret = (uint64_t) (-1);
1840+
}
1841+
return ret;
1842+
}
1843+
17921844
int main(int argc, char** argv)
17931845
{
17941846
int ret = 0;
@@ -1936,6 +1988,35 @@ int main(int argc, char** argv)
19361988
CMD.policy_sign = 1;
19371989
CMD.policy_file = argv[++i];
19381990
}
1991+
else if (strcmp(argv[i], "--custom-tlv") == 0) {
1992+
int p = CMD.custom_tlvs;
1993+
uint16_t tag, len;
1994+
if (p >= MAX_CUSTOM_TLVS) {
1995+
fprintf(stderr, "Too many custom TLVs.\n");
1996+
exit(16);
1997+
}
1998+
if (argc < (i + 3)) {
1999+
fprintf(stderr, "Invalid custom TLV fields. \n");
2000+
exit(16);
2001+
}
2002+
tag = (uint16_t)arg2num(argv[i + 1], 2);
2003+
len = (uint16_t)arg2num(argv[i + 2], 2);
2004+
2005+
if ((tag < 0x0030) || (tag > 0xFEFE)) {
2006+
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
2007+
exit(16);
2008+
}
2009+
if ((len != 1) && (len != 2) && (len != 4) && (len != 8)) {
2010+
fprintf(stderr, "Invalid custom tag len: %s\n", argv[i + 2]);
2011+
fprintf(stderr, "Accepted len: 1, 2, 4 or 8\n");
2012+
exit(16);
2013+
}
2014+
CMD.custom_tlv[p].tag = tag;
2015+
CMD.custom_tlv[p].len = len;
2016+
CMD.custom_tlv[p].val = arg2num(argv[i+3], len);
2017+
CMD.custom_tlvs++;
2018+
i += 3;
2019+
}
19392020
else {
19402021
i--;
19412022
break;
@@ -2010,6 +2091,18 @@ int main(int argc, char** argv)
20102091
printf("(bootloader)");
20112092
printf("\n");
20122093

2094+
if (CMD.custom_tlvs > 0) {
2095+
uint32_t i;
2096+
printf("Custom TLVS: %u\n", CMD.custom_tlvs);
2097+
for (i = 0; i < CMD.custom_tlvs; i++) {
2098+
printf("TLV %u\n", i);
2099+
printf("----\n");
2100+
printf("Tag: %04X Len: %hu Val: %" PRIu64 "\n", CMD.custom_tlv[i].tag,
2101+
CMD.custom_tlv[i].len, CMD.custom_tlv[i].val);
2102+
printf("-----\n");
2103+
}
2104+
}
2105+
20132106
/* get header and signature sizes */
20142107
if (CMD.sign == SIGN_ED25519) {
20152108
if (CMD.header_sz < 256)

0 commit comments

Comments
 (0)