Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/test-custom-tlv-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,51 @@ jobs:
- name: Run get_tlv simulator test
run: |
[ x`./wolfboot.elf get_tlv 2>/dev/null| tail -1` = xAABBCCDDEEFF0011223344 ]
custom_tlv_large_simulator_tests:
runs-on: ubuntu-latest
container:
image: ghcr.io/wolfssl/wolfboot-ci-sim:v1.0
timeout-minutes: 15

steps:
- uses: actions/checkout@v4
with:
submodules: true

- name: Trust workspace
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: make clean
run: |
make distclean
- name: Select config
run: |
cp config/examples/sim.config .config
- name: Build tools
run: |
make -C tools/keytools && make -C tools/bin-assemble
- name: Build wolfboot.elf and test-app/image.elf with a 2KB header
run: |
make clean && make IMAGE_HEADER_SIZE=2048
- name: Sign the image with 300-byte buffer and file custom TLVs
run: |
VAL=`printf 'A5%.0s' $(seq 1 300)`
head -c 300 /dev/zero | tr '\0' '\245' > large_tlv.bin
IMAGE_HEADER_SIZE=2048 tools/keytools/sign --ed25519 --custom-tlv-buffer 0x0034 $VAL --custom-tlv-file 0x0035 large_tlv.bin test-app/image.elf wolfboot_signing_private_key.der 1
- name: Re-assemble the internal_flash.dd image file
run: |
make assemble_internal_flash.dd IMAGE_HEADER_SIZE=2048
- name: Run get_tlv simulator test on the 300-byte buffer TLV
run: |
[ x`./wolfboot.elf get_tlv 2>/dev/null| tail -1` = x`printf 'A5%.0s' $(seq 1 300)` ]
- name: Run get_tlv simulator test on the 300-byte file TLV (tag 0x35)
run: |
[ x`./wolfboot.elf get_tlv=53 2>/dev/null| tail -1` = x`printf 'A5%.0s' $(seq 1 300)` ]
17 changes: 15 additions & 2 deletions docs/Signing.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,17 +255,30 @@ Provides a value to be set with a custom tag
* `--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.
is implicit, and is the length of the value. The maximum length is 65535 bytes.
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.

* `--custom-tlv-string tag ascii-string`: Adds a TLV entry with arbitrary length to the manifest
header, corresponding to the type identified by `tag`, and assigns the value of `ascii-string`. 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 `ascii-string`. `ascii-string` argument is in the form of a string,
is implicit, and is the length of the `ascii-string`. The maximum length is 65535 bytes.
`ascii-string` argument is in the form of a string,
e.g. `--custom-tlv-string 0x0030 "Version-1"` will add a TLV entry with tag 0x0030,
length 9 and value Version-1.

* `--custom-tlv-file tag filename`: Adds a TLV entry with arbitrary length to the manifest
header, corresponding to the type identified by `tag`, with the value read as raw bytes
from the file `filename`. The tag is a 16-bit number. Valid tags are in the range between
0x0030 and 0xFEFE. The length is implicit, and is the size of the file. The maximum length
is 65535 bytes. Unlike `--custom-tlv-buffer`, the value is not passed on the command line,
so large binary values are not subject to the OS argument length limits.

If the custom TLVs do not fit in the configured header size, the sign tool automatically
increases the size of the manifest header, rounding up to the next power of two. wolfBoot
must be built with a matching `IMAGE_HEADER_SIZE`, or it will fail to locate the firmware
image at boot.

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

If the private key is not accessible, while it's possible to sign payloads using
Expand Down
214 changes: 149 additions & 65 deletions tools/keytools/sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -1377,42 +1377,46 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,

/* Check certificate chain file size before allocating header, and adjust
* header size if needed */
if (CMD.cert_chain_file != NULL) {
struct stat file_stat;

/* Get the file size */
if (stat(CMD.cert_chain_file, &file_stat) == 0) {
off_t chain_file_sz = file_stat.st_size;
uint32_t required_space;

if ((chain_file_sz < 0) ||
((uintmax_t)chain_file_sz > (uintmax_t)UINT32_MAX)) {
printf("Warning: certificate chain file size is invalid (%jd)\n",
(intmax_t)chain_file_sz);
if ((CMD.cert_chain_file != NULL) || (CMD.custom_tlvs > 0)) {
uint32_t cert_chain_sz = 0;
Comment thread
bigbrett marked this conversation as resolved.
Outdated
uint32_t required_space;

/* Get the certificate chain file size */
if (CMD.cert_chain_file != NULL) {
struct stat file_stat;
if (stat(CMD.cert_chain_file, &file_stat) == 0) {
off_t chain_file_sz = file_stat.st_size;
if ((chain_file_sz < 0) ||
((uintmax_t)chain_file_sz > (uintmax_t)UINT32_MAX)) {
printf("Warning: certificate chain file size is invalid "
"(%jd)\n", (intmax_t)chain_file_sz);
}
else {
cert_chain_sz = (uint32_t)chain_file_sz;
}
Comment thread
bigbrett marked this conversation as resolved.
}
else {
required_space = header_required_size(is_diff,
(uint32_t)chain_file_sz, secondary_key_sz);

/* If the current header size is too small, increase it */
if (CMD.header_sz < required_space) {
/* Round up to nearest power of 2 that can hold the chain */
const uint32_t min_header_size = 256;
uint32_t new_size = min_header_size;
while (new_size < required_space) {
new_size *= 2;
}

printf("Increasing header size from %u to %u bytes to fit "
"certificate chain\n",
CMD.header_sz, new_size);
CMD.header_sz = new_size;
}
printf("Warning: Could not stat certificate chain file %s: %s\n",
CMD.cert_chain_file, strerror(errno));
}
}
else {
printf("Warning: Could not stat certificate chain file %s: %s\n",
CMD.cert_chain_file, strerror(errno));

required_space = header_required_size(is_diff, cert_chain_sz,
secondary_key_sz);

/* If the current header size is too small, increase it */
if (CMD.header_sz < required_space) {
/* Round up to nearest power of 2 that can hold all fields */
const uint32_t min_header_size = 256;
uint32_t new_size = min_header_size;
while (new_size < required_space) {
new_size *= 2;
}
Comment thread
bigbrett marked this conversation as resolved.

printf("Increasing header size from %u to %u bytes to fit "
"manifest header fields\n",
CMD.header_sz, new_size);
CMD.header_sz = new_size;
}
}

Expand Down Expand Up @@ -2530,35 +2534,37 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
len3++;
}
/* make_header_delta() below calls make_header_ex(is_diff=1), which may grow
* CMD.header_sz to fit the delta TLVs plus certificate chain. Resolve that
* expansion here, using the same logic, so patch_inv_off reflects the header
* size actually written; otherwise HDR_IMG_DELTA_INVERSE would encode a
* stale, too-small offset and break inverse-patch rollback. */
if (CMD.cert_chain_file != NULL) {
struct stat cc_stat;
if ((stat(CMD.cert_chain_file, &cc_stat) == 0) &&
(cc_stat.st_size >= 0)) {
if ((uintmax_t)cc_stat.st_size > (uintmax_t)UINT16_MAX) {
printf("Error: Certificate chain too large for TLV encoding "
"(%ju > %u)\n", (uintmax_t)cc_stat.st_size, UINT16_MAX);
goto cleanup;
* CMD.header_sz to fit the delta TLVs, custom TLVs and certificate chain.
* Resolve that expansion here, using the same logic, so patch_inv_off
* reflects the header size actually written; otherwise HDR_IMG_DELTA_INVERSE
* would encode a stale, too-small offset and break inverse-patch rollback. */
if ((CMD.cert_chain_file != NULL) || (CMD.custom_tlvs > 0)) {
uint32_t cert_chain_sz = 0;
uint32_t required_space;
if (CMD.cert_chain_file != NULL) {
struct stat cc_stat;
if ((stat(CMD.cert_chain_file, &cc_stat) == 0) &&
(cc_stat.st_size >= 0)) {
if ((uintmax_t)cc_stat.st_size > (uintmax_t)UINT16_MAX) {
printf("Error: Certificate chain too large for TLV encoding "
"(%ju > %u)\n", (uintmax_t)cc_stat.st_size, UINT16_MAX);
goto cleanup;
}
cert_chain_sz = (uint32_t)cc_stat.st_size;
}
else {
uint32_t required_space = header_required_size(1,
(uint32_t)cc_stat.st_size, 0);
if (CMD.header_sz < required_space) {
uint32_t new_size = 256;
while (new_size < required_space) {
if (new_size > (UINT32_MAX / 2U)) {
printf("Error: Header size overflow while sizing "
"certificate chain\n");
goto cleanup;
}
new_size *= 2;
}
CMD.header_sz = new_size;
}
required_space = header_required_size(1, cert_chain_sz, 0);
if (CMD.header_sz < required_space) {
uint32_t new_size = 256;
while (new_size < required_space) {
if (new_size > (UINT32_MAX / 2U)) {
printf("Error: Header size overflow while sizing "
"manifest header\n");
goto cleanup;
}
new_size *= 2;
}
CMD.header_sz = new_size;
}
}
patch_inv_off = (uint32_t)len3 + CMD.header_sz;
Expand Down Expand Up @@ -3202,6 +3208,7 @@ int main(int argc, char** argv)
} else if (strcmp(argv[i], "--custom-tlv-buffer") == 0) {
int p = CMD.custom_tlvs;
uint16_t tag, len;
size_t slen;
uint32_t j;
if (p >= MAX_CUSTOM_TLVS) {
fprintf(stderr, "Too many custom TLVs.\n");
Expand All @@ -3212,7 +3219,7 @@ int main(int argc, char** argv)
exit(16);
}
Comment thread
bigbrett marked this conversation as resolved.
Outdated
tag = (uint16_t)arg2num(argv[i + 1], 2);
len = (uint16_t)strlen(argv[i + 2]) / 2;
slen = strlen(argv[i + 2]);
Comment thread
bigbrett marked this conversation as resolved.
if (tag < 0x0030) {
Comment thread
bigbrett marked this conversation as resolved.
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
exit(16);
Expand All @@ -3221,10 +3228,13 @@ int main(int argc, char** argv)
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]);
if ((slen / 2) > UINT16_MAX) {
fprintf(stderr, "custom tlv buffer size too big: "
"%lu bytes (max %u)\n", (unsigned long)(slen / 2),
UINT16_MAX);
exit(16);
}
Comment thread
bigbrett marked this conversation as resolved.
len = (uint16_t)(slen / 2);
Comment thread
bigbrett marked this conversation as resolved.
CMD.custom_tlv[p].tag = tag;
CMD.custom_tlv[p].len = len;
CMD.custom_tlv[p].buffer = malloc(len);
Expand All @@ -3241,6 +3251,7 @@ int main(int argc, char** argv)
} else if (strcmp(argv[i], "--custom-tlv-string") == 0) {
int p = CMD.custom_tlvs;
uint16_t tag, len;
size_t slen;
uint32_t j;
if (p >= MAX_CUSTOM_TLVS) {
fprintf(stderr, "Too many custom TLVs.\n");
Expand All @@ -3251,7 +3262,7 @@ int main(int argc, char** argv)
exit(16);
}
Comment thread
bigbrett marked this conversation as resolved.
Outdated
tag = (uint16_t)arg2num(argv[i + 1], 2);
len = (uint16_t)strlen(argv[i + 2]);
slen = strlen(argv[i + 2]);
Comment thread
bigbrett marked this conversation as resolved.
if (tag < 0x0030) {
fprintf(stderr, "Invalid custom tag: %s\n", argv[i + 1]);
exit(16);
Expand All @@ -3260,10 +3271,12 @@ int main(int argc, char** argv)
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]);
if (slen > UINT16_MAX) {
fprintf(stderr, "custom tlv string size too big: "
"%lu bytes (max %u)\n", (unsigned long)slen, UINT16_MAX);
exit(16);
}
len = (uint16_t)slen;
Comment thread
bigbrett marked this conversation as resolved.
CMD.custom_tlv[p].tag = tag;
CMD.custom_tlv[p].len = len;
CMD.custom_tlv[p].buffer = malloc(len);
Expand All @@ -3276,6 +3289,68 @@ int main(int argc, char** argv)
}
CMD.custom_tlvs++;
i += 2;
} else if (strcmp(argv[i], "--custom-tlv-file") == 0) {
int p = CMD.custom_tlvs;
uint16_t tag;
FILE *f;
long fsz;
size_t rd;
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);
}
Comment thread
bigbrett marked this conversation as resolved.
Outdated
tag = (uint16_t)arg2num(argv[i + 1], 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);
}
f = fopen(argv[i + 2], "rb");
if (f == NULL) {
fprintf(stderr, "Cannot open custom tlv file %s: %s\n",
argv[i + 2], strerror(errno));
exit(16);
}
fseek(f, 0, SEEK_END);
fsz = ftell(f);
fseek(f, 0, SEEK_SET);
Comment thread
bigbrett marked this conversation as resolved.
if (fsz <= 0) {
fprintf(stderr, "custom tlv file is empty or unreadable: %s\n",
argv[i + 2]);
fclose(f);
exit(16);
}
Comment thread
bigbrett marked this conversation as resolved.
if (fsz > UINT16_MAX) {
fprintf(stderr, "custom tlv file too big: %ld bytes "
"(max %u): %s\n", fsz, UINT16_MAX, argv[i + 2]);
fclose(f);
exit(16);
}
CMD.custom_tlv[p].tag = tag;
CMD.custom_tlv[p].len = (uint16_t)fsz;
CMD.custom_tlv[p].buffer = malloc((size_t)fsz);
if (CMD.custom_tlv[p].buffer == NULL) {
fprintf(stderr, "Error malloc for custom tlv buffer %ld\n",
fsz);
fclose(f);
exit(16);
}
rd = fread(CMD.custom_tlv[p].buffer, 1, (size_t)fsz, f);
fclose(f);
if (rd != (size_t)fsz) {
fprintf(stderr, "Error reading custom tlv file %s\n",
argv[i + 2]);
exit(16);
}
CMD.custom_tlvs++;
i += 2;
}
else if (strcmp(argv[i], "--cert-chain") == 0) {
if (argc <= (i + 1)) {
Expand Down Expand Up @@ -3395,11 +3470,20 @@ int main(int argc, char** argv)
printf("TLV %u\n", i);
printf("----\n");
if (CMD.custom_tlv[i].buffer) {
/* only print the first 256 bytes of large values */
uint16_t print_len = CMD.custom_tlv[i].len;
if (print_len > 256) {
print_len = 256;
}
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++) {
for (j = 0; j < print_len; j++) {
printf("%02X", CMD.custom_tlv[i].buffer[j]);
}
if (print_len < CMD.custom_tlv[i].len) {
printf("... (truncated, %hu bytes total)",
CMD.custom_tlv[i].len);
}
printf("\n");

} else {
Expand Down
1 change: 1 addition & 0 deletions tools/unit-tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ run: $(TESTS)
python3 unit-sign-delta-tlv.py || exit 1
python3 unit-sign-delta-cert-inv-off.py || exit 1
python3 unit-sign-custom-tlv-le.py || exit 1
python3 unit-sign-custom-tlv-large.py || exit 1


WOLFCRYPT_SRC:=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha.c \
Expand Down
Loading
Loading