Skip to content

Commit dff83a8

Browse files
authored
Merge pull request #412 from danielinux/custom-tlv
Added support for custom TLVs in manifest header
2 parents 69556cd + 0996e0f commit dff83a8

File tree

5 files changed

+239
-2
lines changed

5 files changed

+239
-2
lines changed

.github/workflows/test-keytools.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,30 @@ jobs:
237237
run: |
238238
./tools/keytools/keygen --id 1,3,5,10,11,13,14 --ecc256 -g wolfboot_signing_private_key.der | grep "mask" | grep "00006c2a"
239239
240+
# Custom TLVs
241+
- name: make clean
242+
run: |
243+
make distclean
244+
245+
- name: Select config
246+
run: |
247+
cp config/examples/sim.config .config && make include/target.h
248+
249+
- name: Build tools
250+
run: |
251+
make -C tools/keytools && make -C tools/bin-assemble
252+
253+
- name: Build wolfboot with ECC256/SHA256
254+
run: |
255+
make SIGN=ECC256 HASH=SHA256
256+
257+
- name: Sign app with custom numeric TLV included
258+
run: |
259+
./tools/keytools/sign --ecc256 --sha256 --custom-tlv 0x45 4 0x6f616943 test-app/image.elf wolfboot_signing_private_key.der 2
260+
grep "Ciao" test-app/image_v2_signed.bin
261+
262+
- name: Sign app with custom buffer TLV included
263+
run: |
264+
./tools/keytools/sign --ecc256 --sha256 --custom-tlv-buffer 0x46 48656C6C6F20776F726C64 test-app/image.elf wolfboot_signing_private_key.der 3
265+
grep "Hello world" test-app/image_v3_signed.bin
240266

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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,23 @@ 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+
* `--custom-tlv-buffer tag value`: Adds a TLV entry with arbitrary length to the manifest
190+
header, corresponding to the type identified by `tag`, and assigns the value `value`. The
191+
tag is a 16-bit number. Valid tags are in the range between 0x0030 and 0xFEFE. The length
192+
is implicit, and is the length of the value.
193+
Value argument is in the form of a hex string, e.g. `--custom-tlv-buffer 0x0030 AABBCCDDEE`
194+
will add a TLV entry with tag 0x0030, length 5 and value 0xAABBCCDDEE.
195+
196+
180197
#### Three-steps signing using external provisioning tools
181198

182199
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: 155 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,13 @@ 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+
uint8_t *buffer;
277+
} custom_tlv[MAX_CUSTOM_TLVS];
268278
};
269279

270280
static struct cmd_options CMD = {
@@ -273,6 +283,7 @@ static struct cmd_options CMD = {
273283
.hash_algo = HASH_SHA256,
274284
.header_sz = IMAGE_HEADER_SIZE,
275285
.partition_id = HDR_IMG_TYPE_APP
286+
276287
};
277288

278289
static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
@@ -1067,6 +1078,28 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
10671078
&patch_inv_len);
10681079
}
10691080

1081+
/* Add custom TLVs */
1082+
if (CMD.custom_tlvs > 0) {
1083+
uint32_t i;
1084+
for (i = 0; i < CMD.custom_tlvs; i++) {
1085+
if (CMD.custom_tlv[i].len == 8) {
1086+
/* This field requires 8-byte alignment */
1087+
while((header_idx % 8) != 4)
1088+
header_idx++;
1089+
}
1090+
if (CMD.custom_tlv[i].buffer == NULL) {
1091+
header_append_tag(header, &header_idx, CMD.custom_tlv[i].tag,
1092+
CMD.custom_tlv[i].len, &CMD.custom_tlv[i].val);
1093+
} else {
1094+
header_append_tag(header, &header_idx, CMD.custom_tlv[i].tag,
1095+
CMD.custom_tlv[i].len, CMD.custom_tlv[i].buffer);
1096+
}
1097+
}
1098+
/* Align for next field */
1099+
while ((header_idx % 4) != 0)
1100+
header_idx++;
1101+
}
1102+
10701103
/* Add padding bytes. Sha-3 val field requires 8-byte alignment */
10711104
while ((header_idx % 8) != 4)
10721105
header_idx++;
@@ -1789,6 +1822,31 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
17891822
return ret;
17901823
}
17911824

1825+
uint64_t arg2num(const char *arg, size_t len)
1826+
{
1827+
uint64_t ret = (uint64_t) -1;
1828+
if (strncmp(arg, "0x", 2) == 0) {
1829+
ret = strtoll(arg + 2, NULL, 16);
1830+
} else {
1831+
ret = strtoll(arg, NULL, 10);
1832+
}
1833+
switch (len) {
1834+
case 1:
1835+
ret &= 0xFF;
1836+
break;
1837+
case 2:
1838+
ret &= 0xFFFF;
1839+
break;
1840+
case 4:
1841+
ret &= 0xFFFFFFFF;
1842+
case 8:
1843+
break;
1844+
default:
1845+
ret = (uint64_t) (-1);
1846+
}
1847+
return ret;
1848+
}
1849+
17921850
int main(int argc, char** argv)
17931851
{
17941852
int ret = 0;
@@ -1936,6 +1994,81 @@ int main(int argc, char** argv)
19361994
CMD.policy_sign = 1;
19371995
CMD.policy_file = argv[++i];
19381996
}
1997+
else if (strcmp(argv[i], "--custom-tlv") == 0) {
1998+
int p = CMD.custom_tlvs;
1999+
uint16_t tag, len;
2000+
if (p >= MAX_CUSTOM_TLVS) {
2001+
fprintf(stderr, "Too many custom TLVs.\n");
2002+
exit(16);
2003+
}
2004+
if (argc < (i + 3)) {
2005+
fprintf(stderr, "Invalid custom TLV fields. \n");
2006+
exit(16);
2007+
}
2008+
tag = (uint16_t)arg2num(argv[i + 1], 2);
2009+
len = (uint16_t)arg2num(argv[i + 2], 2);
2010+
2011+
if (tag < 0x0030) {
2012+
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
2013+
exit(16);
2014+
}
2015+
if ( ((tag & 0xFF00) == 0xFF00) || ((tag & 0xFF) == 0xFF) ) {
2016+
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
2017+
exit(16);
2018+
}
2019+
2020+
if ((len != 1) && (len != 2) && (len != 4) && (len != 8)) {
2021+
fprintf(stderr, "Invalid custom tag len: %s\n", argv[i + 2]);
2022+
fprintf(stderr, "Accepted len: 1, 2, 4 or 8\n");
2023+
exit(16);
2024+
}
2025+
2026+
CMD.custom_tlv[p].tag = tag;
2027+
CMD.custom_tlv[p].len = len;
2028+
CMD.custom_tlv[p].val = arg2num(argv[i+3], len);
2029+
CMD.custom_tlv[p].buffer = NULL;
2030+
CMD.custom_tlvs++;
2031+
i += 3;
2032+
} else if (strcmp(argv[i], "--custom-tlv-buffer") == 0) {
2033+
int p = CMD.custom_tlvs;
2034+
uint16_t tag, len;
2035+
uint32_t j;
2036+
if (p >= MAX_CUSTOM_TLVS) {
2037+
fprintf(stderr, "Too many custom TLVs.\n");
2038+
exit(16);
2039+
}
2040+
if (argc < (i + 2)) {
2041+
fprintf(stderr, "Invalid custom TLV fields. \n");
2042+
exit(16);
2043+
}
2044+
tag = (uint16_t)arg2num(argv[i + 1], 2);
2045+
len = (uint16_t)strlen(argv[i + 2]) / 2;
2046+
if (tag < 0x0030) {
2047+
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
2048+
exit(16);
2049+
}
2050+
if ( ((tag & 0xFF00) == 0xFF00) || ((tag & 0xFF) == 0xFF) ) {
2051+
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
2052+
exit(16);
2053+
}
2054+
if (len > 255) {
2055+
fprintf(stderr, "custom tlv buffer size too big: %s\n", argv[i + 2]);
2056+
exit(16);
2057+
}
2058+
CMD.custom_tlv[p].tag = tag;
2059+
CMD.custom_tlv[p].len = len;
2060+
CMD.custom_tlv[p].buffer = malloc(len);
2061+
if (CMD.custom_tlv[p].buffer == NULL) {
2062+
fprintf(stderr, "Error malloc for custom tlv buffer %d\n", len);
2063+
exit(16);
2064+
}
2065+
for (j = 0; j < len; j++) {
2066+
char c[3] = {argv[i + 2][j * 2], argv[i + 2][j * 2 + 1], 0};
2067+
CMD.custom_tlv[p].buffer[j] = (uint8_t)strtol(c, NULL, 16);
2068+
}
2069+
CMD.custom_tlvs++;
2070+
i += 2;
2071+
}
19392072
else {
19402073
i--;
19412074
break;
@@ -2010,6 +2143,28 @@ int main(int argc, char** argv)
20102143
printf("(bootloader)");
20112144
printf("\n");
20122145

2146+
if (CMD.custom_tlvs > 0) {
2147+
uint32_t i, j;
2148+
printf("Custom TLVS: %u\n", CMD.custom_tlvs);
2149+
for (i = 0; i < CMD.custom_tlvs; i++) {
2150+
printf("TLV %u\n", i);
2151+
printf("----\n");
2152+
if (CMD.custom_tlv[i].buffer) {
2153+
printf("Tag: %04X Len: %hu Val: ", CMD.custom_tlv[i].tag,
2154+
CMD.custom_tlv[i].len);
2155+
for (j = 0; j < CMD.custom_tlv[i].len; j++) {
2156+
printf("%02X", CMD.custom_tlv[i].buffer[j]);
2157+
}
2158+
printf("\n");
2159+
2160+
} else {
2161+
printf("Tag: %04X Len: %hu Val: %" PRIu64 "\n", CMD.custom_tlv[i].tag,
2162+
CMD.custom_tlv[i].len, CMD.custom_tlv[i].val);
2163+
}
2164+
printf("-----\n");
2165+
}
2166+
}
2167+
20132168
/* get header and signature sizes */
20142169
if (CMD.sign == SIGN_ED25519) {
20152170
if (CMD.header_sz < 256)

0 commit comments

Comments
 (0)