/* sign.c * * C native signing tool * * * Copyright (C) 2021 wolfSSL Inc. * * This file is part of wolfBoot. * * wolfBoot is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ /* Option to enable sign tool debugging */ /* Must also define DEBUG_WOLFSSL in user_settings.h */ //#define DEBUG_SIGNTOOL #ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE /* unlink */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wolfboot/version.h" #ifdef DEBUG_SIGNTOOL #define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__) #define DEBUG_BUFFER(buf,sz) WOLFSSL_BUFFER(buf,sz) #else #define DEBUG_PRINT(...) do{}while(0) #define DEBUG_BUFFER(buf,sz) do{}while(0) #endif #ifdef _WIN32 #include #define HAVE_MMAP 0 #define ftruncate(fd, len) _chsize(fd, len) static inline int fp_truncate(FILE *f, size_t len) { int fd; if (f == NULL) return -1; fd = _fileno(f); return _chsize(fd, len); } #else #define HAVE_MMAP 1 #include #include #endif #define MAX_SRC_SIZE (1 << 24) #ifndef MAX_CUSTOM_TLVS #define MAX_CUSTOM_TLVS (16) #endif #include #include #include #ifdef HAVE_CHACHA #include #endif #ifndef NO_RSA #include #endif #ifdef HAVE_ECC #include #endif #ifdef HAVE_ED25519 #include #endif #ifdef HAVE_ED448 #include #endif #ifndef NO_SHA256 #include #endif #ifndef NO_SHA384 #include #endif #ifdef WOLFSSL_SHA3 #include #endif #include #include #ifdef WOLFSSL_HAVE_LMS #include #ifdef HAVE_LIBLMS #include #else #include #endif #endif #ifdef WOLFSSL_HAVE_XMSS #include #ifdef HAVE_LIBXMSS #include #else #include #endif #endif #ifdef WOLFSSL_WC_DILITHIUM #include #endif #ifdef DEBUG_SIGNTOOL #include #endif #include "wolfboot/wolfboot.h" #if defined(_WIN32) && !defined(PATH_MAX) #define PATH_MAX 256 #endif #define WOLFBOOT_MAGIC 0x464C4F57 /* WOLF */ #define HDR_VERSION 0x01 #define HDR_TIMESTAMP 0x02 #define HDR_SHA256 0x03 #define HDR_IMG_TYPE 0x04 #define HDR_PUBKEY 0x10 #define HDR_SECONDARY_CIPHER 0x11 #define HDR_SECONDARY_PUBKEY 0x12 #define HDR_SHA3_384 0x13 #define HDR_SHA384 0x14 #define HDR_SIGNATURE 0x20 #define HDR_POLICY_SIGNATURE 0x21 #define HDR_SECONDARY_SIGNATURE 0x22 #define HDR_SHA256_LEN 32 #define HDR_SHA384_LEN 48 #define HDR_SHA3_384_LEN 48 #define HDR_VERSION_LEN 4 #define HDR_TIMESTAMP_LEN 8 #define HDR_IMG_TYPE_LEN 2 #define HDR_IMG_DELTA_BASE 0x05 #define HDR_IMG_DELTA_SIZE 0x06 #define HDR_IMG_DELTA_BASE_HASH 0x07 #define HDR_IMG_DELTA_INVERSE 0x15 #define HDR_IMG_DELTA_INVERSE_SIZE 0x16 #define HDR_IMG_TYPE_AUTH_MASK 0xFF00 #define HDR_IMG_TYPE_AUTH_NONE 0xFF00 #define HDR_IMG_TYPE_WOLFBOOT 0x0000 #define HDR_IMG_TYPE_APP 0x0001 #define HDR_IMG_TYPE_DIFF 0x00D0 #define HDR_IMG_TYPE_HYBRID 0x0080 #define HASH_SHA256 HDR_SHA256 #define HASH_SHA384 HDR_SHA384 #define HASH_SHA3 HDR_SHA3_384 #define SIGN_AUTO 0 #define NO_SIGN HDR_IMG_TYPE_AUTH_NONE #define SIGN_ED25519 HDR_IMG_TYPE_AUTH_ED25519 #define SIGN_ECC256 HDR_IMG_TYPE_AUTH_ECC256 #define SIGN_RSA2048 HDR_IMG_TYPE_AUTH_RSA2048 #define SIGN_RSA3072 HDR_IMG_TYPE_AUTH_RSA3072 #define SIGN_RSA4096 HDR_IMG_TYPE_AUTH_RSA4096 #define SIGN_ED448 HDR_IMG_TYPE_AUTH_ED448 #define SIGN_ECC384 HDR_IMG_TYPE_AUTH_ECC384 #define SIGN_ECC521 HDR_IMG_TYPE_AUTH_ECC521 #define SIGN_LMS HDR_IMG_TYPE_AUTH_LMS #define SIGN_XMSS HDR_IMG_TYPE_AUTH_XMSS #define SIGN_ML_DSA HDR_IMG_TYPE_AUTH_ML_DSA #define ENC_OFF 0 #define ENC_CHACHA 1 #define ENC_AES128 2 #define ENC_AES256 3 #define ENC_BLOCK_SIZE 16 #define ENC_MAX_KEY_SZ 32 #define ENC_MAX_IV_SZ 16 static void header_append_u32(uint8_t* header, uint32_t* idx, uint32_t tmp32) { memcpy(&header[*idx], &tmp32, sizeof(tmp32)); *idx += sizeof(tmp32); } static void header_append_u16(uint8_t* header, uint32_t* idx, uint16_t tmp16) { memcpy(&header[*idx], &tmp16, sizeof(tmp16)); *idx += sizeof(tmp16); } static void header_append_tag(uint8_t* header, uint32_t* idx, uint16_t tag, uint16_t len, void* data) { header_append_u16(header, idx, tag); header_append_u16(header, idx, len); memcpy(&header[*idx], data, len); *idx += len; } #include "../lms/lms_common.h" #include "../xmss/xmss_common.h" /* Globals */ static const char wolfboot_delta_file[] = "/tmp/wolfboot-delta.bin"; static struct { ed25519_key ed; ed448_key ed4; ecc_key ecc; RsaKey rsa; LmsKey lms; XmssKey xmss; MlDsaKey ml_dsa; } key; struct cmd_options { int manual_sign; int policy_sign; int self_update; int sha_only; int encrypt; int hash_algo; int sign; int hybrid; int secondary_sign; int delta; int no_ts; int sign_wenc; const char *image_file; const char *key_file; const char *secondary_key_file; const char *fw_version; const char *signature_file; const char *policy_file; const char *encrypt_key_file; const char *delta_base_file; int no_base_sha; char output_image_file[PATH_MAX]; char output_diff_file[PATH_MAX]; char output_encrypted_image_file[PATH_MAX]; uint32_t pubkey_sz; uint32_t header_sz; uint32_t signature_sz; uint32_t secondary_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 = { .sign = SIGN_AUTO, .encrypt = ENC_OFF, .hash_algo = HASH_SHA256, .partition_id = HDR_IMG_TYPE_APP, .hybrid = 0 }; static uint16_t sign_tool_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr) { uint8_t *p = haystack; uint16_t len, htype; const volatile uint8_t *max_p = (haystack - IMAGE_HEADER_OFFSET) + CMD.header_sz; *ptr = NULL; if (p > max_p) { fprintf(stderr, "Illegal address (too high)\n"); return 0; } while ((p + 4) < max_p) { htype = p[0] | (p[1] << 8); if (htype == 0) { fprintf(stderr, "Explicit end of options reached\n"); break; } /* skip unaligned half-words and padding bytes */ if ((p[0] == HDR_PADDING) || ((((size_t)p) & 0x01) != 0)) { p++; continue; } len = p[2] | (p[3] << 8); /* check len */ if ((4 + len) > (uint16_t)(CMD.header_sz - IMAGE_HEADER_OFFSET)) { fprintf(stderr, "This field too large to fit into header " "(%d > %d)\n", (int)(4 + len), (int)(CMD.header_sz - IMAGE_HEADER_OFFSET)); break; } /* check max pointer */ if (p + 4 + len > max_p) { fprintf(stderr, "This field is too large and would overflow the " "image header pointer\n"); break; } /* skip header [type|len] */ p += 4; if (htype == type) { /* found, return pointer to data portion */ *ptr = p; return len; } p += len; } return 0; } static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id, uint32_t header_sz, uint8_t **key_buffer, uint32_t *key_buffer_sz, uint8_t **pubkey, uint32_t *pubkey_sz, int secondary) { int ret = -1; int initRet = -1; uint32_t idx; uint32_t qxSz = curve_sz; uint32_t qySz = curve_sz; *pubkey_sz = curve_sz * 2; *pubkey = malloc(*pubkey_sz); /* assume malloc works */ initRet = ret = wc_ecc_init(&key.ecc); if (CMD.manual_sign || CMD.sha_only) { /* raw (public x + public y) */ if (*key_buffer_sz == (curve_sz * 2)) { memcpy(*pubkey, *key_buffer, *pubkey_sz); ret = 0; } else { if (ret == 0) { idx = 0; ret = wc_EccPublicKeyDecode(*key_buffer, &idx, &key.ecc, *key_buffer_sz); } /* we could decode another type of key in auto so check */ if (ret == 0 && key.ecc.dp->id != curve_id) { ret = -1; } if (ret == 0) { ret = wc_ecc_export_public_raw(&key.ecc, *pubkey, &qxSz, /* public x */ *pubkey + curve_sz, &qySz /* public y */ ); } } } /* raw only (public x + public y + private d)*/ else if (*key_buffer_sz == (curve_sz * 3)) { memcpy(*pubkey, *key_buffer, *pubkey_sz); if (ret == 0) { ret = wc_ecc_import_unsigned(&key.ecc, *key_buffer, /* public x */ (*key_buffer) + curve_sz, /* public y */ (*key_buffer) + (curve_sz * 2), /* private d */ curve_id ); if (ret == 0) { /* don't free the key */ initRet = 0; } } } /* try ASN.1/DER decode of private key */ else { if (ret == 0) { idx = 0; ret = wc_EccPrivateKeyDecode(*key_buffer, &idx, &key.ecc, *key_buffer_sz); } /* we could decode another type of key in auto so check */ if (ret == 0 && key.ecc.dp->id != curve_id) { ret = -1; } if (ret == 0) { ret = wc_ecc_export_public_raw(&key.ecc, *pubkey, &qxSz, /* public x */ *pubkey + curve_sz, &qySz /* public y */ ); } if (ret == 0) { /* don't free the key */ initRet = 0; } } if (ret != 0 && initRet == 0) { wc_ecc_free(&key.ecc); } if (ret != 0) free(*pubkey); if (ret == 0 || CMD.sign != SIGN_AUTO) { if (CMD.header_sz < header_sz) CMD.header_sz = header_sz; if (secondary) { CMD.secondary_sign = sign_type; CMD.secondary_signature_sz = (curve_sz * 2); printf("Secondary ECC key, size: %d\n", CMD.secondary_signature_sz); } else { CMD.sign = sign_type; CMD.signature_sz = (curve_sz * 2); } ret = 0; } return ret; } static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz, uint32_t header_sz, uint8_t **key_buffer, uint32_t *key_buffer_sz, uint8_t **pubkey, uint32_t *pubkey_sz, int secondary) { int ret = -1; int initRet = -1; uint32_t idx; uint32_t keySzOut = 0; if (CMD.manual_sign || CMD.sha_only) { /* use public key directly */ *pubkey = *key_buffer; *pubkey_sz = *key_buffer_sz; if (*pubkey_sz <= rsa_pubkeysz) { CMD.header_sz = header_sz; if (secondary) { CMD.secondary_signature_sz = rsa_keysz; CMD.secondary_sign = sign_type; } else { CMD.sign = sign_type; CMD.signature_sz = rsa_keysz; } } ret = 0; } else { initRet = ret = wc_InitRsaKey(&key.rsa, NULL); if (ret == 0) { idx = 0; ret = wc_RsaPrivateKeyDecode(*key_buffer, &idx, &key.rsa, *key_buffer_sz); } if (ret == 0) { ret = wc_RsaKeyToPublicDer(&key.rsa, *key_buffer, *key_buffer_sz); } if (ret > 0) { *pubkey = *key_buffer; *pubkey_sz = ret; ret = 0; } if (ret == 0) { keySzOut = wc_RsaEncryptSize(&key.rsa); } if (ret != 0 && initRet == 0) { wc_FreeRsaKey(&key.rsa); } if (ret == 0 || CMD.sign != SIGN_AUTO) { if (CMD.header_sz < header_sz) CMD.header_sz = header_sz; if (secondary) { CMD.secondary_sign = sign_type; CMD.secondary_signature_sz = keySzOut; } else { CMD.sign = sign_type; CMD.signature_sz = keySzOut; } printf("Found RSA%d key\n", keySzOut*8); } } return ret; } static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, uint8_t **pubkey, uint32_t *pubkey_sz, int secondary) { int ret = -1; int initRet = -1; uint32_t idx = 0; int io_sz; FILE *f; word32 priv_sz = 0; word32 pub_sz = 0; int sign = CMD.sign; const char *key_file = CMD.key_file; /* open and load key buffer */ *key_buffer = NULL; if (secondary) { key_file = CMD.secondary_key_file; sign = CMD.secondary_sign; } f = fopen(key_file, "rb"); if (f == NULL) { printf("Open key file %s failed\n", key_file); goto failure; } fseek(f, 0, SEEK_END); *key_buffer_sz = ftell(f); fseek(f, 0, SEEK_SET); *key_buffer = malloc(*key_buffer_sz); if (*key_buffer) { io_sz = (int)fread(*key_buffer, 1, *key_buffer_sz, f); if (io_sz != (int)*key_buffer_sz) { printf("Key file read error!\n"); goto failure; } } fclose(f); if (*key_buffer == NULL) { printf("Key buffer malloc error!\n"); goto failure; } DEBUG_PRINT("Key buffer size: %d\n", *key_buffer_sz); switch (sign) { /* auto, just try them all, no harm no foul */ default: FALL_THROUGH; case SIGN_ED25519: ret = -1; initRet = -1; *pubkey_sz = ED25519_PUB_KEY_SIZE; *pubkey = malloc(*pubkey_sz); if (CMD.manual_sign || CMD.sha_only) { /* raw */ if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_ED25519) { memcpy(*pubkey, *key_buffer, KEYSTORE_PUBKEY_SIZE_ED25519); ret = 0; } else { initRet = ret = wc_ed25519_init(&key.ed); if (ret == 0) { idx = 0; ret = wc_Ed25519PublicKeyDecode(*key_buffer, &idx, &key.ed, *key_buffer_sz); } if (ret == 0) { ret = wc_ed25519_export_public(&key.ed, *pubkey, pubkey_sz); } /* free key no matter what */ if (initRet == 0) wc_ed25519_free(&key.ed); } } /* raw only */ else if (*key_buffer_sz == ED25519_PRV_KEY_SIZE) { memcpy(*pubkey, *key_buffer + ED25519_KEY_SIZE, KEYSTORE_PUBKEY_SIZE_ED25519); initRet = ret = wc_ed25519_init(&key.ed); if (ret == 0) { ret = wc_ed25519_import_private_key(*key_buffer, ED25519_KEY_SIZE, *pubkey, *pubkey_sz, &key.ed); } /* only free the key if we failed after allocating */ if (ret != 0 && initRet == 0) wc_ed25519_free(&key.ed); } if (ret != 0) free(*pubkey); /* break if we succeed or are not using auto */ if (ret == 0 || sign != SIGN_AUTO) { if (CMD.header_sz < 256) CMD.header_sz = 256; if (secondary) CMD.secondary_signature_sz = 64; else CMD.signature_sz = 64; sign = SIGN_ED25519; printf("Found ED25519 key\n"); break; } FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_ED448: ret = -1; initRet = -1; *pubkey_sz = ED448_PUB_KEY_SIZE; *pubkey = malloc(*pubkey_sz); if (CMD.manual_sign || CMD.sha_only) { /* raw */ if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_ED448) { memcpy(*pubkey, *key_buffer, KEYSTORE_PUBKEY_SIZE_ED448); ret = 0; } else { initRet = ret = wc_ed448_init(&key.ed4); if (ret == 0) { idx = 0; ret = wc_Ed448PublicKeyDecode(*key_buffer, &idx, &key.ed4, *key_buffer_sz); } if (ret == 0) { ret = wc_ed448_export_public(&key.ed4, *pubkey, pubkey_sz); } /* free key no matter what */ if (initRet == 0) wc_ed448_free(&key.ed4); } } /* raw only */ else if (*key_buffer_sz == ED448_PRV_KEY_SIZE) { memcpy(*pubkey, *key_buffer + ED448_KEY_SIZE, ED448_PUB_KEY_SIZE); initRet = ret = wc_ed448_init(&key.ed4); if (ret == 0) { ret = wc_ed448_import_private_key(*key_buffer, ED448_KEY_SIZE, *pubkey, *pubkey_sz, &key.ed4); } /* only free the key if we failed after allocating */ if (ret != 0 && initRet == 0) wc_ed448_free(&key.ed4); } if (ret != 0) free(*pubkey); /* break if we succeed or are not using auto */ if (ret == 0 || sign != SIGN_AUTO) { if (CMD.header_sz < 512) CMD.header_sz = 512; if (secondary) CMD.secondary_signature_sz = 114; else CMD.signature_sz = 114; sign = SIGN_ED448; printf("Found ED448 key\n"); break; } FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_ECC256: ret = load_key_ecc(SIGN_ECC256, 32, ECC_SECP256R1, 256, key_buffer, key_buffer_sz, pubkey, pubkey_sz, secondary); if (ret == 0) break; FALL_THROUGH; case SIGN_ECC384: ret = load_key_ecc(SIGN_ECC384, 48, ECC_SECP384R1, 512, key_buffer, key_buffer_sz, pubkey, pubkey_sz, secondary); if (ret == 0) break; FALL_THROUGH; case SIGN_ECC521: ret = load_key_ecc(SIGN_ECC521, 66, ECC_SECP521R1, 512, key_buffer, key_buffer_sz, pubkey, pubkey_sz, secondary); if (ret == 0) break; FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_RSA2048: ret = load_key_rsa(SIGN_RSA2048, 256, KEYSTORE_PUBKEY_SIZE_RSA2048, 512, key_buffer, key_buffer_sz, pubkey, pubkey_sz, secondary); if (ret == 0) break; FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_RSA3072: ret = load_key_rsa(SIGN_RSA3072, 384, KEYSTORE_PUBKEY_SIZE_RSA3072, 512, key_buffer, key_buffer_sz, pubkey, pubkey_sz, secondary); if (ret == 0) break; FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_RSA4096: ret = load_key_rsa(SIGN_RSA4096, 512, KEYSTORE_PUBKEY_SIZE_RSA4096, 1024, key_buffer, key_buffer_sz, pubkey, pubkey_sz, secondary); if (ret == 0) break; FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_LMS: ret = -1; if (sign == SIGN_AUTO) { /* LMS is stateful and requires additional config, and is not * compatible with SIGN_AUTO. */ printf("error: SIGN_AUTO with LMS is not supported\n"); break; } /* The LMS file callbacks will handle writing and reading the * private key. We only need to set the public key here. * * If both priv/pub are present: * - The first 64 bytes is the private key. * - The next 60 bytes is the public key. */ if (*key_buffer_sz == (HSS_MAX_PRIVATE_KEY_LEN + KEYSTORE_PUBKEY_SIZE_LMS)) { /* priv + pub */ *pubkey = (*key_buffer) + HSS_MAX_PRIVATE_KEY_LEN; *pubkey_sz = (*key_buffer_sz) - HSS_MAX_PRIVATE_KEY_LEN; ret = 0; printf("Found LMS key\n"); break; } else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_LMS) { /* pub only */ *pubkey = (*key_buffer); *pubkey_sz = KEYSTORE_PUBKEY_SIZE_LMS; ret = 0; printf("Found LMS public only key\n"); break; } else { /* We don't recognize this as an LMS pub or private key. */ printf("error: unrecognized LMS key size: %d\n", *key_buffer_sz); } FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_XMSS: ret = -1; if (sign == SIGN_AUTO) { /* XMSS is stateful and requires additional config, and is not * compatible with SIGN_AUTO. */ printf("error: SIGN_AUTO with XMSS is not supported\n"); break; } /* The XMSS file callbacks will handle writing and reading the * private key. We only need to set the public key here. * * If both priv/pub are present: * - The first ?? bytes is the private key. * - The next 68 bytes is the public key. */ ret = wc_XmssKey_GetPrivLen(&key.xmss, &priv_sz); if (ret != 0 || priv_sz <= 0) { printf("error: wc_XmssKey_GetPrivLen returned %d\n", ret); break; } DEBUG_PRINT("info: xmss sk len: %d\n", priv_sz); DEBUG_PRINT("info: xmss pk len: %d\n", KEYSTORE_PUBKEY_SIZE_XMSS); if (*key_buffer_sz == (priv_sz + KEYSTORE_PUBKEY_SIZE_XMSS)) { /* priv + pub */ *pubkey = (*key_buffer) + priv_sz; *pubkey_sz = (*key_buffer_sz) - priv_sz; ret = 0; printf("Found XMSS key\n"); break; } else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_XMSS) { /* pub only */ *pubkey = (*key_buffer); *pubkey_sz = KEYSTORE_PUBKEY_SIZE_XMSS; ret = 0; printf("Found XMSS public only key\n"); break; } else { /* We don't recognize this as an XMSS pub or private key. */ printf("error: unrecognized XMSS key size: %d\n", *key_buffer_sz); } FALL_THROUGH; /* we didn't solve the key, keep trying */ case SIGN_ML_DSA: ret = wc_MlDsaKey_GetPubLen(&key.ml_dsa, (int *)&pub_sz); if (ret != 0 || pub_sz <= 0) { printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret); break; } /* Get the ML-DSA private key length. This API returns * the public + private length. */ ret = wc_MlDsaKey_GetPrivLen(&key.ml_dsa, (int*)&priv_sz); if (ret != 0 || priv_sz <= 0) { printf("error: wc_MlDsaKey_GetPrivLen returned %d\n", ret); break; } if (priv_sz <= pub_sz) { printf("error: ml-dsa: unexpected key lengths: %d, %d", priv_sz, pub_sz); break; } else { priv_sz -= pub_sz; } DEBUG_PRINT("info: ml-dsa priv len: %d\n", priv_sz); DEBUG_PRINT("info: ml-dsa pub len: %d\n", pub_sz); if (*key_buffer_sz == (priv_sz + pub_sz)) { /* priv + pub */ ret = wc_MlDsaKey_ImportPrivRaw(&key.ml_dsa, *key_buffer, priv_sz); *pubkey = (*key_buffer) + priv_sz; *pubkey_sz = (*key_buffer_sz) - priv_sz; ret = 0; printf("Found ml-dsa key\n"); break; } else if (*key_buffer_sz == pub_sz) { /* pub only */ *pubkey = (*key_buffer); *pubkey_sz = pub_sz; ret = 0; printf("Found ml-dsa public only key\n"); break; } else { /* We don't recognize this as an ML-DSA pub or private key. */ printf("error: unrecognized ml-dsa key size: %d\n", *key_buffer_sz); ret = -1; } break; } /* end switch (sign) */ if (ret != 0) { printf("Key decode error %d\n", ret); goto failure; } DEBUG_PRINT("Pubkey %d\n", *pubkey_sz); DEBUG_BUFFER(*pubkey, *pubkey_sz); return *key_buffer; failure: if (*key_buffer != NULL) { free(*key_buffer); *key_buffer = NULL; } return NULL; } /* Sign the digest */ static int sign_digest(int sign, int hash_algo, uint8_t* signature, uint32_t* signature_sz, uint8_t* digest, uint32_t digest_sz, int secondary) { int ret; WC_RNG rng; printf("Sign: %02x\n", sign >> 8); (void)secondary; if ((ret = wc_InitRng(&rng)) != 0) { return ret; } if (sign == SIGN_ED25519) { ret = wc_ed25519_sign_msg(digest, digest_sz, signature, signature_sz, &key.ed); } else if (sign == SIGN_ED448) { ret = wc_ed448_sign_msg(digest, digest_sz, signature, signature_sz, &key.ed4, NULL, 0); } else if (sign == SIGN_ECC256 || sign == SIGN_ECC384 || sign == SIGN_ECC521) { mp_int r, s; int keySz; if (sign == SIGN_ECC256) keySz = 32; if (sign == SIGN_ECC384) keySz = 48; if (sign == SIGN_ECC521) keySz = 66; *signature_sz = keySz*2; memset(signature, 0, *signature_sz); mp_init(&r); mp_init(&s); ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &key.ecc, &r, &s); if (ret == 0) { word32 rSz, sSz; /* export sign r/s - zero pad to key size */ rSz = mp_unsigned_bin_size(&r); mp_to_unsigned_bin(&r, &signature[keySz - rSz]); sSz = mp_unsigned_bin_size(&s); mp_to_unsigned_bin(&s, &signature[keySz + (keySz - sSz)]); } mp_clear(&r); mp_clear(&s); } else if (sign == SIGN_RSA2048 || sign == SIGN_RSA3072 || sign == SIGN_RSA4096) { #ifndef WC_MAX_ENCODED_DIG_ASN_SZ #define WC_MAX_ENCODED_DIG_ASN_SZ 9 /* enum(bit or octet) + length(4) */ #endif uint8_t buf[WC_MAX_DIGEST_SIZE + WC_MAX_ENCODED_DIG_ASN_SZ]; uint32_t enchash_sz = digest_sz; uint8_t* enchash = digest; if (CMD.sign_wenc) { /* add ASN.1 signature encoding */ int hashOID = 0; if (hash_algo == HASH_SHA256) hashOID = SHA256h; else if (hash_algo == HASH_SHA384) hashOID = SHA384h; else if (hash_algo == HASH_SHA3) hashOID = SHA3_384h; enchash_sz = wc_EncodeSignature(buf, digest, digest_sz, hashOID); enchash = buf; } ret = wc_RsaSSL_Sign(enchash, enchash_sz, signature, *signature_sz, &key.rsa, &rng); if (ret > 0) { *signature_sz = ret; ret = 0; } } else if (sign == SIGN_LMS) { const char *key_file = CMD.key_file; if (secondary) { key_file = CMD.secondary_key_file; } /* Set the callbacks, so LMS can update the private key while signing */ ret = wc_LmsKey_SetWriteCb(&key.lms, lms_write_key); if (ret == 0) { ret = wc_LmsKey_SetReadCb(&key.lms, lms_read_key); } if (ret == 0) { ret = wc_LmsKey_SetContext(&key.lms, (void*)key_file); } if (ret == 0) { ret = wc_LmsKey_Reload(&key.lms); } if (ret == 0) { ret = wc_LmsKey_Sign(&key.lms, signature, signature_sz, digest, digest_sz); } if (ret != 0) { fprintf(stderr, "error signing with LMS: %d\n", ret); } } else if (sign == SIGN_XMSS) { const char *key_file = CMD.key_file; if (secondary) { key_file = CMD.secondary_key_file; } ret = wc_XmssKey_Init(&key.xmss, NULL, INVALID_DEVID); /* Set the callbacks, so XMSS can update the private key while signing */ if (ret == 0) { ret = wc_XmssKey_SetWriteCb(&key.xmss, xmss_write_key); } if (ret == 0) { ret = wc_XmssKey_SetReadCb(&key.xmss, xmss_read_key); } if (ret == 0) { ret = wc_XmssKey_SetContext(&key.xmss, (void*)key_file); } if (ret == 0) { ret = wc_XmssKey_SetParamStr(&key.xmss, WOLFBOOT_XMSS_PARAMS); } if (ret == 0) { ret = wc_XmssKey_Reload(&key.xmss); } if (ret == 0) { ret = wc_XmssKey_Sign(&key.xmss, signature, signature_sz, digest, digest_sz); } if (ret != 0) { fprintf(stderr, "error signing with XMSS: %d\n", ret); } } else if (sign == SIGN_ML_DSA) { /* Nothing else to do, ready to sign. */ if (ret == 0) { ret = wc_MlDsaKey_Sign(&key.ml_dsa, signature, signature_sz, digest, digest_sz, &rng); } if (ret != 0) { fprintf(stderr, "error signing with ML-DSA: %d\n", ret); } } else { ret = NOT_COMPILED_IN; } wc_FreeRng(&rng); return ret; } #define ALIGN_8(x) while ((x % 8) != 4) { x++; } #define ALIGN_4(x) while ((x % 4) != 0) { x++; } static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, const char *image_file, const char *outfile, uint32_t delta_base_version, uint32_t patch_len, uint32_t patch_inv_off, uint32_t patch_inv_len, const uint8_t *secondary_key, uint32_t secondary_key_sz, uint8_t *base_hash, uint32_t base_hash_sz) { uint32_t header_idx; uint8_t *header; FILE *f, *f2, *fek, *fef; uint32_t fw_version32; struct stat attrib; uint16_t image_type; uint8_t* signature = NULL; uint8_t* secondary_signature = NULL; uint8_t* policy = NULL; int ret = -1; uint8_t buf[4096]; uint8_t second_buf[4096]; uint32_t read_sz, pos; uint8_t digest[48]; /* max digest */ uint32_t digest_sz = 0; uint32_t image_sz = 0; int io_sz; header_idx = 0; header = malloc(CMD.header_sz); if (header == NULL) { printf("Header malloc error!\n"); goto failure; } memset(header, 0xFF, CMD.header_sz); /* Get size of image */ f = fopen(image_file, "rb"); if (f == NULL) { printf("Open image file %s failed\n", image_file); goto failure; } fseek(f, 0, SEEK_END); image_sz = ftell(f); fseek(f, 0, SEEK_SET); fclose(f); /* Append Magic header (spells 'WOLF') */ header_append_u32(header, &header_idx, WOLFBOOT_MAGIC); /* Append Image size */ header_append_u32(header, &header_idx, image_sz); /* No pad bytes, version is aligned */ /* Append Version field */ fw_version32 = strtol(CMD.fw_version, NULL, 10); header_append_tag(header, &header_idx, HDR_VERSION, HDR_VERSION_LEN, &fw_version32); /* Append pad bytes, so timestamp val field is 8-byte aligned */ ALIGN_8(header_idx); if (!CMD.no_ts) { /* Append Timestamp field */ stat(image_file, &attrib); header_append_tag(header, &header_idx, HDR_TIMESTAMP, HDR_TIMESTAMP_LEN, &attrib.st_ctime); } /* Append Image type field */ image_type = (uint16_t)CMD.sign & HDR_IMG_TYPE_AUTH_MASK; image_type |= CMD.partition_id; if (is_diff) image_type |= HDR_IMG_TYPE_DIFF; header_append_tag(header, &header_idx, HDR_IMG_TYPE, HDR_IMG_TYPE_LEN, &image_type); if (is_diff) { /* Append pad bytes, so fields are 4-byte aligned */ ALIGN_4(header_idx); header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE, 4, &delta_base_version); header_append_tag(header, &header_idx, HDR_IMG_DELTA_SIZE, 4, &patch_len); /* Append pad bytes, so fields are 4-byte aligned */ ALIGN_4(header_idx); header_append_tag(header, &header_idx, HDR_IMG_DELTA_INVERSE, 4, &patch_inv_off); header_append_tag(header, &header_idx, HDR_IMG_DELTA_INVERSE_SIZE, 4, &patch_inv_len); if (!CMD.no_base_sha) { /* Append pad bytes, so base hash is 8-byte aligned */ ALIGN_8(header_idx); if (!base_hash) { fprintf(stderr, "Base hash for delta image not found.\n"); exit(1); } if (CMD.hash_algo == HASH_SHA256) { if (base_hash_sz != HDR_SHA256_LEN) { fprintf(stderr, "Invalid base hash size for SHA256.\n"); exit(1); } header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH, HDR_SHA256_LEN, base_hash); } else if (CMD.hash_algo == HASH_SHA384) { if (base_hash_sz != HDR_SHA384_LEN) { fprintf(stderr, "Invalid base hash size for SHA384.\n"); exit(1); } header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH, HDR_SHA384_LEN, base_hash); } else if (CMD.hash_algo == HASH_SHA3) { if (base_hash_sz != HDR_SHA3_384_LEN) { fprintf(stderr, "Invalid base hash size for SHA3-384.\n"); exit(1); } header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH, HDR_SHA3_384_LEN, base_hash); } } } /* Add custom TLVs */ if (CMD.custom_tlvs > 0) { uint32_t i; for (i = 0; i < CMD.custom_tlvs; i++) { /* require 8-byte alignment */ /* The offset '4' takes into account 2B Tag + 2B Len, so that the * Value starts at (addr % 8 == 0) position. */ ALIGN_8(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); } } } /* Add padding bytes. Sha-3 val field requires 8-byte alignment */ /* The offset '4' takes into account 2B Tag + 2B Len, so that the Value * starts at (addr % 8 == 0) position. */ ALIGN_8(header_idx); /* Calculate hashes */ if (CMD.hash_algo == HASH_SHA256) { #ifndef NO_SHA256 wc_Sha256 sha; digest_sz = SHA256_DIGEST_SIZE; /* pubkey hash calculation */ ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID); if (ret == 0) { printf("Hashing primary pubkey, size: %d\n", pubkey_sz); ret = wc_Sha256Update(&sha, pubkey, pubkey_sz); if (ret == 0) wc_Sha256Final(&sha, buf); wc_Sha256Free(&sha); /* Add Pubkey Hash to header */ header_append_tag(header, &header_idx, HDR_PUBKEY, digest_sz, buf); DEBUG_PRINT("Pubkey hash %d\n", digest_sz); DEBUG_BUFFER(buf, digest_sz); ALIGN_8(header_idx); } /* secondary public key in hybrid mode */ if (ret == 0 && CMD.hybrid && secondary_key_sz > 0) { ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID); DEBUG_PRINT("Hashing secondary pubkey, size: %d\n", secondary_key_sz); if (ret == 0) { ret = wc_Sha256Update(&sha, secondary_key, secondary_key_sz); if (ret == 0) wc_Sha256Final(&sha, second_buf); wc_Sha256Free(&sha); /* Add Secondary cipher to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_CIPHER, 2, &CMD.secondary_sign); ALIGN_8(header_idx); /* Add Secondary Pubkey Hash to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_PUBKEY, digest_sz, second_buf); DEBUG_PRINT("Secondary pubkey hash %d\n", digest_sz); DEBUG_BUFFER(second_buf, digest_sz); } } if (ret == 0) { printf("Calculating SHA256 digest...\n"); ALIGN_8(header_idx); ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID); } if (ret == 0) { ALIGN_8(header_idx); /* Hash Header */ ret = wc_Sha256Update(&sha, header, header_idx); /* Hash image file */ f = fopen(image_file, "rb"); pos = 0; while (ret == 0 && pos < image_sz) { read_sz = image_sz - pos; if (read_sz > 32) read_sz = 32; io_sz = (int)fread(buf, 1, read_sz, f); if ((io_sz < 0) && !feof(f)) { ret = -1; break; } ret = wc_Sha256Update(&sha, buf, read_sz); pos += read_sz; } fclose(f); if (ret == 0) { wc_Sha256Final(&sha, digest); digest_sz = HDR_SHA256_LEN; } wc_Sha256Free(&sha); } #endif } else if (CMD.hash_algo == HASH_SHA384) { #ifndef NO_SHA384 wc_Sha384 sha; digest_sz = SHA384_DIGEST_SIZE; ALIGN_8(header_idx); printf("Calculating SHA384 digest...\n"); /* pubkey hash calculation */ ret = wc_InitSha384_ex(&sha, NULL, INVALID_DEVID); if (ret == 0) { ret = wc_Sha384Update(&sha, pubkey, pubkey_sz); if (ret == 0) { wc_Sha384Final(&sha, buf); /* Add Pubkey Hash to header */ ALIGN_8(header_idx); header_append_tag(header, &header_idx, HDR_PUBKEY, digest_sz, buf); DEBUG_PRINT("Pubkey hash %d\n", digest_sz); DEBUG_BUFFER(buf, digest_sz); ALIGN_8(header_idx); } wc_Sha384Free(&sha); } if (ret == 0 && CMD.hybrid && secondary_key_sz > 0) { ret = wc_InitSha384_ex(&sha, NULL, INVALID_DEVID); if (ret == 0) { ret = wc_Sha384Update(&sha, secondary_key, secondary_key_sz); if (ret == 0) { wc_Sha384Final(&sha, second_buf); /* Add Secondary cipher to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_CIPHER, 2, &CMD.secondary_sign); /* Add Secondary Pubkey Hash to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_PUBKEY, digest_sz, second_buf); DEBUG_PRINT("Secondary pubkey hash %d\n", digest_sz); DEBUG_BUFFER(second_buf, digest_sz); ALIGN_8(header_idx); } wc_Sha384Free(&sha); } } if (ret == 0) ret = wc_InitSha384_ex(&sha, NULL, INVALID_DEVID); if (ret == 0) { /* Hash Header */ ret = wc_Sha384Update(&sha, header, header_idx); /* Hash image file */ f = fopen(image_file, "rb"); pos = 0; while (ret == 0 && pos < image_sz) { read_sz = image_sz - pos; if (read_sz > 32) read_sz = 32; io_sz = (int)fread(buf, 1, read_sz, f); if ((io_sz < 0) && !feof(f)) { ret = -1; break; } ret = wc_Sha384Update(&sha, buf, read_sz); pos += read_sz; } fclose(f); if (ret == 0) { wc_Sha384Final(&sha, digest); digest_sz = HDR_SHA384_LEN; } wc_Sha384Free(&sha); } #endif } else if (CMD.hash_algo == HASH_SHA3) { #ifdef WOLFSSL_SHA3 wc_Sha3 sha; digest_sz = HDR_SHA3_384_LEN; ret = wc_InitSha3_384(&sha, NULL, INVALID_DEVID); if (ret == 0) { ret = wc_Sha3_384_Update(&sha, pubkey, pubkey_sz); if (ret == 0) { ret = wc_Sha3_384_Final(&sha, buf); header_append_tag(header, &header_idx, HDR_PUBKEY, digest_sz, buf); DEBUG_PRINT("Pubkey hash %d\n", digest_sz); DEBUG_BUFFER(buf, digest_sz); ALIGN_8(header_idx); } wc_Sha3_384_Free(&sha); } if (ret == 0 && CMD.hybrid && secondary_key_sz > 0) { ALIGN_8(header_idx); ret = wc_InitSha3_384(&sha, NULL, INVALID_DEVID); if (ret == 0) { ret = wc_Sha3_384_Update(&sha, secondary_key, secondary_key_sz); if (ret == 0) { ret = wc_Sha3_384_Final(&sha, second_buf); /* Add Secondary cipher to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_CIPHER, 2, &CMD.secondary_sign); header_append_tag(header, &header_idx, HDR_SECONDARY_PUBKEY, digest_sz, second_buf); DEBUG_PRINT("Secondary pubkey hash %d\n", digest_sz); DEBUG_BUFFER(second_buf, digest_sz); ALIGN_8(header_idx); } wc_Sha3_384_Free(&sha); } } if (ret == 0) { printf("Calculating SHA3 digest...\n"); ALIGN_8(header_idx); ret = wc_InitSha3_384(&sha, NULL, INVALID_DEVID); } if (ret == 0) { /* Hash Header */ ret = wc_Sha3_384_Update(&sha, header, header_idx); /* Hash image file */ f = fopen(image_file, "rb"); pos = 0; while (ret == 0 && pos < image_sz) { read_sz = image_sz - pos; if (read_sz > 128) read_sz = 128; io_sz = (int)fread(buf, 1, read_sz, f); if ((io_sz < 0) && !feof(f)) { ret = -1; break; } ret = wc_Sha3_384_Update(&sha, buf, read_sz); pos += read_sz; } fclose(f); if (ret == 0) { ret = wc_Sha3_384_Final(&sha, digest); digest_sz = HDR_SHA3_384_LEN; } wc_Sha3_384_Free(&sha); } #endif } if (digest_sz == 0) { printf("Hash algorithm error %d\n", ret); goto failure; } DEBUG_PRINT("Image hash %d\n", digest_sz); DEBUG_BUFFER(digest, digest_sz); /* Add image hash to header */ header_append_tag(header, &header_idx, CMD.hash_algo, digest_sz, digest); if (CMD.sign != NO_SIGN) { /* If hash only, then save digest and exit */ if (CMD.sha_only) { f = fopen(outfile, "wb"); if (f == NULL) { printf("Open output file %s failed\n", outfile); goto failure; } fwrite(digest, 1, digest_sz, f); fclose(f); printf("Digest image %s successfully created.\n", outfile); exit(0); } /* save max sig size */ CMD.policy_sz = CMD.signature_sz; /* Signing Image */ signature = malloc(CMD.signature_sz); if (signature == NULL) { printf("Signature malloc error!\n"); goto failure; } memset(signature, 0, CMD.signature_sz); DEBUG_PRINT("Signature sz (malloc): %d\n", CMD.signature_sz); if (!CMD.manual_sign) { printf("Signing the digest...\n"); DEBUG_PRINT("Digest %d\n", digest_sz); DEBUG_BUFFER(digest, digest_sz); /* Sign the digest */ ret = sign_digest(CMD.sign, CMD.hash_algo, signature, &CMD.signature_sz, digest, digest_sz, 0); if (ret != 0) { printf("Signing error %d\n", ret); goto failure; } } else { DEBUG_PRINT("Opening signature file %s\n", CMD.signature_file); f = fopen(CMD.signature_file, "rb"); if (f == NULL) { printf("Open signature file %s failed\n", CMD.signature_file); goto failure; } io_sz = (int)fread(signature, 1, CMD.signature_sz, f); fclose(f); if (io_sz <= 0) { printf("Error reading file %s\n", CMD.signature_file); goto failure; } CMD.signature_sz = io_sz; } if (CMD.hybrid) { /* Sign the digest again with the secondary key */ secondary_signature = malloc(CMD.secondary_signature_sz); if (secondary_signature == NULL) { printf("Secondary Signature malloc error!\n"); goto failure; } memset(secondary_signature, 0, CMD.secondary_signature_sz); ret = sign_digest(CMD.secondary_sign, CMD.hash_algo, secondary_signature, &CMD.secondary_signature_sz, digest, digest_sz, 1); if (ret != 0) { printf("Secondary Signing error %d\n", ret); goto failure; } } /* Signing Policy */ if (CMD.policy_sign) { /* Policy is always SHA2-256 */ digest_sz = HDR_SHA256_LEN; policy = malloc(CMD.policy_sz + sizeof(uint32_t)); if (policy == NULL) { printf("Policy Signature malloc error!\n"); goto failure; } memset(policy, 0, CMD.policy_sz); /* open policy file */ printf("Opening policy file %s\n", CMD.policy_file); f = fopen(CMD.policy_file, "rb"); if (f == NULL) { printf("Open policy digest file %s failed\n", CMD.policy_file); goto failure; } /* policy file starts with 4 byte PCR mask */ io_sz = (int)fread(policy, 1, sizeof(uint32_t), f); if (io_sz != sizeof(uint32_t)) { printf("Error reading file %s\n", CMD.policy_file); fclose(f); goto failure; } if (!CMD.manual_sign) { /* in normal sign mode PCR digest (32 bytes) */ io_sz = (int)fread(digest, 1, digest_sz, f); fclose(f); if (io_sz != (int)digest_sz) { printf("Error reading file %s\n", CMD.policy_file); goto failure; } printf("Signing the policy digest...\n"); DEBUG_PRINT("Policy Digest %d\n", digest_sz); DEBUG_BUFFER(digest, digest_sz); /* Policy is always SHA2-256 */ ret = sign_digest(CMD.sign, HASH_SHA256, policy + sizeof(uint32_t), &CMD.policy_sz, digest, digest_sz, 0); if (ret != 0) { printf("Signing policy error %d\n", ret); goto failure; } } else { /* in manual mode remainder is PCR signature */ io_sz = (int)fread(policy, 1, CMD.policy_sz, f); fclose(f); if (io_sz <= 0) { printf("Error reading file %s\n", CMD.policy_file); goto failure; } CMD.policy_sz = io_sz; } /* save copy of signed policy including 4 byte header to file */ snprintf((char*)buf, sizeof(buf), "%s.sig", CMD.policy_file); printf("Saving policy signature to %s\n", (char*)buf); f = fopen((char*)buf, "w+b"); if (f != NULL) { fwrite(policy, 1, CMD.policy_sz + sizeof(uint32_t), f); fclose(f); } } DEBUG_PRINT("Signature %d\n", CMD.signature_sz); DEBUG_BUFFER(signature, CMD.signature_sz); if (CMD.policy_sign) { DEBUG_PRINT("PCR Mask 0x%08x\n", *((uint32_t*)policy)); DEBUG_PRINT("Policy Signature %d\n", CMD.policy_sz); DEBUG_BUFFER(policy + sizeof(uint32_t), CMD.policy_sz); } /* Add signature to header */ ALIGN_8(header_idx); header_append_tag(header, &header_idx, HDR_SIGNATURE, CMD.signature_sz, signature); if (CMD.hybrid) { /* Add secondary signature to header */ ALIGN_8(header_idx); header_append_tag(header, &header_idx, HDR_SECONDARY_SIGNATURE, CMD.secondary_signature_sz, secondary_signature); } if (CMD.policy_sign) { ALIGN_8(header_idx); /* Add policy signature to header */ header_append_tag(header, &header_idx, HDR_POLICY_SIGNATURE, CMD.policy_sz + (uint16_t)sizeof(uint32_t), policy); } } /* end if(sign != NO_SIGN) */ /* Add padded header at end */ while (header_idx < CMD.header_sz) { header[header_idx++] = 0xFF; } /* Create output image */ f = fopen(outfile, "w+b"); if (f == NULL) { printf("Open output image file %s failed\n", outfile); goto failure; } fwrite(header, 1, header_idx, f); /* Copy image to output */ f2 = fopen(image_file, "rb"); pos = 0; while (pos < image_sz) { read_sz = image_sz; if (read_sz > sizeof(buf)) read_sz = sizeof(buf); read_sz = (uint32_t)fread(buf, 1, read_sz, f2); if ((read_sz == 0) && (feof(f2))) break; fwrite(buf, 1, read_sz, f); pos += read_sz; } if ((CMD.encrypt != ENC_OFF) && CMD.encrypt_key_file) { uint8_t key[ENC_MAX_KEY_SZ], iv[ENC_MAX_IV_SZ]; uint8_t enc_buf[ENC_BLOCK_SIZE]; int ivSz, keySz; uint32_t fsize = 0; switch (CMD.encrypt) { case ENC_CHACHA: ivSz = CHACHA_IV_BYTES; keySz = CHACHA_MAX_KEY_SZ; break; case ENC_AES128: ivSz = 16; keySz = 16; break; case ENC_AES256: ivSz = 16; keySz = 32; break; default: printf("No valid encryption mode selected\n"); goto failure; } fek = fopen(CMD.encrypt_key_file, "rb"); if (fek == NULL) { fprintf(stderr, "Open encryption key file %s: %s\n", CMD.encrypt_key_file, strerror(errno)); exit(1); } ret = (int)fread(key, 1, keySz, fek); if (ret != keySz) { fprintf(stderr, "Error reading key from %s\n", CMD.encrypt_key_file); exit(1); } ret = (int)fread(iv, 1, ivSz, fek); if (ret != ivSz) { fprintf(stderr, "Error reading IV from %s\n", CMD.encrypt_key_file); exit(1); } fclose(fek); fef = fopen(CMD.output_encrypted_image_file, "wb"); if (!fef) { fprintf(stderr, "Open encrypted output file %s: %s\n", CMD.encrypt_key_file, strerror(errno)); } fsize = ftell(f); fseek(f, 0, SEEK_SET); /* restart the _signed file from 0 */ if (CMD.encrypt == ENC_CHACHA) { ChaCha cha; #ifndef HAVE_CHACHA fprintf(stderr, "Encryption not supported: chacha support not found" "in wolfssl configuration.\n"); exit(100); #endif wc_Chacha_SetKey(&cha, key, sizeof(key)); wc_Chacha_SetIV(&cha, iv, 0); for (pos = 0; pos < fsize; pos += ENC_BLOCK_SIZE) { int fread_retval; fread_retval = (int)fread(buf, 1, ENC_BLOCK_SIZE, f); if ((fread_retval == 0) && feof(f)) { break; } wc_Chacha_Process(&cha, enc_buf, buf, fread_retval); fwrite(enc_buf, 1, fread_retval, fef); } } else if ((CMD.encrypt == ENC_AES128) || (CMD.encrypt == ENC_AES256)) { Aes aes_e; wc_AesInit(&aes_e, NULL, 0); wc_AesSetKeyDirect(&aes_e, key, keySz, iv, AES_ENCRYPTION); for (pos = 0; pos < fsize; pos += ENC_BLOCK_SIZE) { int fread_retval; fread_retval = (int)fread(buf, 1, ENC_BLOCK_SIZE, f); if ((fread_retval == 0) && feof(f)) { break; } /* Pad with FF if input is too short */ while((fread_retval % ENC_BLOCK_SIZE) != 0) { buf[fread_retval++] = 0xFF; } wc_AesCtrEncrypt(&aes_e, enc_buf, buf, fread_retval); fwrite(enc_buf, 1, fread_retval, fef); } } fclose(fef); } printf("Output image(s) successfully created.\n"); ret = 0; fclose(f2); fclose(f); failure: if (policy) free(policy); if (header) free(header); return ret; } static int make_header(uint8_t *pubkey, uint32_t pubkey_sz, const char *image_file, const char *outfile) { return make_header_ex(0, pubkey, pubkey_sz, image_file, outfile, 0, 0, 0, 0, NULL, 0, NULL, 0); } static int make_header_delta(uint8_t *pubkey, uint32_t pubkey_sz, const char *image_file, const char *outfile, uint32_t delta_base_version, uint32_t patch_len, uint32_t patch_inv_off, uint32_t patch_inv_len, uint8_t *base_hash, uint32_t base_hash_sz) { return make_header_ex(1, pubkey, pubkey_sz, image_file, outfile, delta_base_version, patch_len, patch_inv_off, patch_inv_len, NULL, 0, base_hash, base_hash_sz); } static int make_hybrid_header(uint8_t *pubkey, uint32_t pubkey_sz, const char *image_file, const char *outfile, const uint8_t *secondary_key, uint32_t secondary_key_sz) { return make_header_ex(0, pubkey, pubkey_sz, image_file, outfile, 0, 0, 0, 0, secondary_key, secondary_key_sz, NULL, 0); } static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, int padding) { #if HAVE_MMAP int fd1 = -1, fd2 = -1, fd3 = -1; #else FILE *f1 = NULL, *f2 = NULL, *f3 = NULL; #endif int len1 = 0, len2 = 0, len3 = 0; struct stat st; void *base = NULL; void *buffer = NULL; uint8_t *dest = NULL; uint8_t ff = 0xff; int r; uint32_t patch_sz, patch_inv_sz; uint32_t patch_inv_off; uint32_t *delta_base_version = NULL; uint16_t delta_base_version_sz = 0; WB_DIFF_CTX diff_ctx; int ret = -1; int io_sz; uint8_t *base_hash = NULL; uint16_t base_hash_sz = 0; uint32_t wolfboot_sector_size = 0; uint32_t blksz; wolfboot_sector_size = wb_diff_get_sector_size(); printf("delta update: WOLFBOOT_SECTOR_SIZE: %u\n", wolfboot_sector_size); blksz = wolfboot_sector_size; dest = malloc(wolfboot_sector_size); if (!dest) { printf("Error allocating memory to prepare patch sectors\n"); goto cleanup; } /* Get source file size */ if (stat(f_base, &st) < 0) { printf("Cannot stat %s\n", f_base); goto cleanup; } len1 = st.st_size; if (len1 > MAX_SRC_SIZE) { printf("%s: file too large\n", f_base); goto cleanup; } #if HAVE_MMAP /* Open base image */ fd1 = open(f_base, O_RDWR); if (fd1 < 0) { printf("Cannot open file %s\n", f_base); goto cleanup; } base = mmap(NULL, len1, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0); if (base == (void *)(-1)) { perror("mmap"); goto cleanup; } #else f1 = fopen(f_base, "wb"); if (f1 == NULL) { printf("Cannot open file %s\n", f_base); goto cleanup; } base = malloc(len1); if (base == NULL) { fprintf(stderr, "Error malloc for base %d\n", len1); goto cleanup; } if (len1 != (int)fread(base, len1, 1, f1)) { perror("read of base"); goto cleanup; } #endif /* Check base image version */ delta_base_version_sz = sign_tool_find_header((uint8_t *)base + 8, HDR_VERSION, (void *)&delta_base_version); if ((delta_base_version_sz != sizeof(uint32_t)) || (*delta_base_version == 0)) { printf("Could not read firmware version from base file %s\n", f_base); goto cleanup; } else { printf("Delta base version: %u\n", *delta_base_version); } /* Retrieve the hash digest of the base image */ if (CMD.hash_algo == HASH_SHA256) base_hash_sz = sign_tool_find_header((uint8_t*)base + 8, HDR_SHA256, &base_hash); else if (CMD.hash_algo == HASH_SHA384) base_hash_sz = sign_tool_find_header((uint8_t*)base + 8, HDR_SHA384, &base_hash); else if (CMD.hash_algo == HASH_SHA3) base_hash_sz = sign_tool_find_header((uint8_t*)base + 8, HDR_SHA3_384, &base_hash); #if HAVE_MMAP /* Open second image file */ fd2 = open(CMD.output_image_file, O_RDONLY); if (fd2 < 0) { printf("Cannot open file %s\n", CMD.output_image_file); goto cleanup; } /* Get second file size */ if (stat(CMD.output_image_file, &st) < 0) { printf("Cannot stat %s\n", CMD.output_image_file); goto cleanup; } len2 = st.st_size; buffer = mmap(NULL, len2, PROT_READ, MAP_SHARED, fd2, 0); if (buffer == (void *)(-1)) { perror("mmap"); goto cleanup; } /* Open output file */ fd3 = open(wolfboot_delta_file, O_RDWR|O_CREAT|O_TRUNC, 0660); if (fd3 < 0) { printf("Cannot open file %s for writing\n", wolfboot_delta_file); goto cleanup; } if (len2 <= 0) { printf("Invalid file size: %d\n", len2); goto cleanup; } lseek(fd3, MAX_SRC_SIZE -1, SEEK_SET); io_sz = write(fd3, &ff, 1); if (io_sz != 1) { printf("Could not write to output file: %s\n", strerror(errno)); goto cleanup; } lseek(fd3, 0, SEEK_SET); len3 = 0; #else /* Open second image file */ f2 = fopen(CMD.output_image_file, "rb"); if (f2 == NULL) { printf("Cannot open file %s\n", CMD.output_image_file); goto cleanup; } /* Get second file size */ fseek(f2, 0L, SEEK_END); len2 = ftell(f2); fseek(f2, 0L, SEEK_SET); buffer = malloc(len2); if (buffer == NULL) { fprintf(stderr, "Error malloc for buffer %d\n", len2); goto cleanup; } if (len2 != (int)fread(buffer, len2, 1, f2)) { perror("fread of buffer"); goto cleanup; } /* Open output file */ f3 = fopen(wolfboot_delta_file, "wb"); if (f3 == NULL) { printf("Cannot open file %s for writing\n", wolfboot_delta_file); goto cleanup; } if (len2 <= 0) { goto cleanup; } fseek(f3, MAX_SRC_SIZE -1, SEEK_SET); io_sz = (int)fwrite(&ff, 1, 1, f3); if (io_sz != 1) { goto cleanup; } fseek(f3, 0, SEEK_SET); len3 = 0; #endif /* Direct base->second patch */ if (wb_diff_init(&diff_ctx, base, len1, buffer, len2) < 0) { goto cleanup; } do { r = wb_diff(&diff_ctx, dest, blksz); if (r < 0) goto cleanup; #if HAVE_MMAP io_sz = write(fd3, dest, r); #else io_sz = (int)fwrite(dest, r, 1, f3); #endif if (io_sz != r) { goto cleanup; } len3 += r; } while (r > 0); patch_sz = len3; while ((len3 % padding) != 0) { uint8_t zero = 0; #if HAVE_MMAP io_sz = write(fd3, &zero, 1); #else io_sz = (int)fwrite(&zero, 1, 1, f3); #endif if (io_sz != 1) { goto cleanup; } len3++; } patch_inv_off = (uint32_t)len3 + CMD.header_sz; patch_inv_sz = 0; /* Inverse second->base patch */ if (wb_diff_init(&diff_ctx, buffer, len2, base, len1) < 0) { goto cleanup; } do { r = wb_diff(&diff_ctx, dest, blksz); if (r < 0) goto cleanup; #if HAVE_MMAP io_sz = write(fd3, dest, r); #else io_sz = (int)fwrite(dest, r, 1, f3); #endif if (io_sz != r) { goto cleanup; } patch_inv_sz += r; len3 += r; } while (r > 0); #if HAVE_MMAP if (fd3 >= 0) { if (len3 > 0) { ret = ftruncate(fd3, len3); } close(fd3); fd3 = -1; } #else if (f3 != NULL) { if (len3 > 0) { ret = fp_truncate(f3, len3); } fclose(f3); f3 = NULL; } #endif if (ret != 0) { goto cleanup; } printf("Successfully created output file %s\n", wolfboot_delta_file); /* Create delta file, with header, from the resulting patch */ ret = make_header_delta(pubkey, pubkey_sz, wolfboot_delta_file, CMD.output_diff_file, *delta_base_version, patch_sz, patch_inv_off, patch_inv_sz, base_hash, base_hash_sz); cleanup: if (dest) { free(dest); dest = NULL; } /* Unlink output file */ unlink(wolfboot_delta_file); #if HAVE_MMAP /* Cleanup/close */ if (fd2 >= 0) { if (len2 > 0) { munmap(buffer, len2); } close(fd2); } if (fd1 >= 0) { if (len1 > 0) { munmap(base, len1); } close(fd1); } #else if (f2 != NULL) { if (len2 > 0) { free(buffer); } fclose(f2); } if (f1 != NULL) { if (len1 > 0) { free(base); } fclose(f1); } #endif 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; } static void set_signature_sizes(int secondary) { uint32_t *sz = &CMD.signature_sz; int *sign = &CMD.sign; uint32_t suggested_sz = 0; char *env_image_header_size; if (secondary) { sz = &CMD.secondary_signature_sz; sign = &CMD.secondary_sign; } /* get header and signature sizes */ if (*sign == SIGN_ED25519) { if (CMD.header_sz < 256) CMD.header_sz = 256; *sz = 64; } else if (*sign == SIGN_ED448) { if (CMD.header_sz < 512) CMD.header_sz = 512; *sz = 114; } else if (*sign == SIGN_ECC256) { if (CMD.header_sz < 256) CMD.header_sz = 256; *sz = 64; } else if (*sign == SIGN_ECC384) { if (CMD.header_sz < 512) CMD.header_sz = 512; *sz = 96; } else if (*sign == SIGN_ECC521) { if (CMD.header_sz < 512) CMD.header_sz = 512; *sz = 132; } else if (*sign == SIGN_RSA2048) { if (CMD.header_sz < 512) CMD.header_sz = 512; *sz = 256; } else if (*sign == SIGN_RSA3072) { if ((CMD.header_sz < 1024) && (CMD.hash_algo != HASH_SHA256)) CMD.header_sz = 1024; if (CMD.header_sz < 512) CMD.header_sz = 512; *sz = 384; } else if (*sign == SIGN_RSA4096) { if (CMD.header_sz < 1024) CMD.header_sz = 1024; *sz = 512; } else if (*sign == SIGN_LMS) { int lms_ret = 0; word32 sig_sz = 0; char *lms_levels_str, *lms_height_str, *lms_winternitz_str; int lms_levels, lms_height, lms_winternitz; lms_levels_str = getenv("LMS_LEVELS"); lms_height_str = getenv("LMS_HEIGHT"); lms_winternitz_str = getenv("LMS_WINTERNITZ"); if (!lms_levels_str) lms_levels = LMS_LEVELS; else lms_levels = atoi(lms_levels_str); if (!lms_height_str) lms_height = LMS_HEIGHT; else lms_height = atoi(lms_height_str); if (!lms_winternitz_str) lms_winternitz = LMS_WINTERNITZ; else lms_winternitz = atoi(lms_winternitz_str); lms_ret = wc_LmsKey_Init(&key.lms, NULL, INVALID_DEVID); if (lms_ret != 0) { fprintf(stderr, "error: wc_LmsKey_Init returned %d\n", lms_ret); exit(1); } lms_ret = wc_LmsKey_SetParameters(&key.lms, lms_levels, lms_height, lms_winternitz); if (lms_ret != 0) { fprintf(stderr, "error: wc_LmsKey_SetParameters(%d, %d, %d)" \ " returned %d\n", lms_levels, lms_height, lms_winternitz, lms_ret); exit(1); } printf("info: using LMS parameters: L%d-H%d-W%d\n", lms_levels, lms_height, lms_winternitz); lms_ret = wc_LmsKey_GetSigLen(&key.lms, &sig_sz); if (lms_ret != 0) { fprintf(stderr, "error: wc_LmsKey_GetSigLen returned %d\n", lms_ret); exit(1); } DEBUG_PRINT("info: LMS signature size: %d\n", sig_sz); if (CMD.header_sz < 2 * sig_sz) CMD.header_sz = 2 * sig_sz; *sz = sig_sz; } else if (*sign == SIGN_XMSS) { int xmss_ret = 0; word32 sig_sz = 0; char *xmss_params = NULL; xmss_params = getenv("XMSS_PARAMS"); if (!xmss_params) xmss_params = WOLFBOOT_XMSS_PARAMS; printf("info: using XMSS parameters: %s\n", xmss_params); xmss_ret = wc_XmssKey_Init(&key.xmss, NULL, INVALID_DEVID); if (xmss_ret != 0) { fprintf(stderr, "error: wc_XmssKey_Init returned %d\n", xmss_ret); exit(1); } xmss_ret = wc_XmssKey_SetParamStr(&key.xmss, xmss_params); if (xmss_ret != 0) { fprintf(stderr, "error: wc_XmssKey_SetParamStr(%s)" \ " returned %d\n", xmss_params, xmss_ret); exit(1); } xmss_ret = wc_XmssKey_GetSigLen(&key.xmss, &sig_sz); if (xmss_ret != 0) { fprintf(stderr, "error: wc_XmssKey_GetSigLen returned %d\n", xmss_ret); exit(1); } DEBUG_PRINT("info: XMSS signature size: %d\n", sig_sz); if (CMD.header_sz < 2 * sig_sz) CMD.header_sz = 2 * sig_sz; *sz = sig_sz; } else if (*sign == SIGN_ML_DSA) { int ml_dsa_ret = 0; uint32_t sig_sz = 0; char *env_ml_dsa_level = NULL; int ml_dsa_level = ML_DSA_LEVEL; env_ml_dsa_level = getenv("ML_DSA_LEVEL"); if (env_ml_dsa_level) ml_dsa_level = atoi(env_ml_dsa_level); ml_dsa_ret = wc_MlDsaKey_Init(&key.ml_dsa, NULL, INVALID_DEVID); if (ml_dsa_ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_Init returned %d\n", ml_dsa_ret); exit(1); } ml_dsa_ret = wc_MlDsaKey_SetParams(&key.ml_dsa, ml_dsa_level); if (ml_dsa_ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_SetParamStr(%d)" \ " returned %d\n", ml_dsa_level, ml_dsa_ret); exit(1); } printf("info: using ML-DSA parameters: %d\n", ml_dsa_level); ml_dsa_ret = wc_MlDsaKey_GetSigLen(&key.ml_dsa, (int *)&sig_sz); if (ml_dsa_ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_GetSigLen returned %d\n", ml_dsa_ret); exit(1); } DEBUG_PRINT("info: ML-DSA signature size: %d\n", sig_sz); if (CMD.header_sz < 2 * sig_sz) CMD.header_sz = 2 * sig_sz; *sz = sig_sz; } env_image_header_size = getenv("IMAGE_HEADER_SIZE"); if (env_image_header_size) { suggested_sz = atoi(env_image_header_size); } if (suggested_sz != 0) { if (CMD.header_sz <= suggested_sz) CMD.header_sz = suggested_sz; else printf("Environment variable IMAGE_HEADER_SIZE=%u overridden.\n", suggested_sz); } if ((CMD.header_sz == 256) && (CMD.delta)) { printf("Adjusting header size to fit base image of delta update \n"); CMD.header_sz <<= 1; } printf("Manifest header size: %u\n", CMD.header_sz); } int main(int argc, char** argv) { int ret = 0; int i; char* tmpstr; const char* sign_str = "AUTO"; const char* hash_str = "SHA256"; const char* secondary_sign_str = "NONE"; uint8_t buf[PATH_MAX-32]; /* leave room to avoid "directive output may be truncated" */ uint8_t *pubkey = NULL; uint32_t pubkey_sz = 0; uint8_t *kbuf=NULL, *key_buffer, *key_buffer2; uint32_t key_buffer_sz, key_buffer_sz2; #ifdef DEBUG_SIGNTOOL wolfSSL_Debugging_ON(); #endif printf("wolfBoot KeyTools (Compiled C version)\n"); printf("wolfBoot version %X\n", WOLFBOOT_VERSION); /* Check arguments and print usage */ if (argc < 4 || argc > 14) { printf("Usage: %s [options] image key version\n", argv[0]); printf("For full usage manual, see 'docs/Signing.md'\n"); exit(1); } /* Set initial manifest header size to a minimum default value */ CMD.header_sz = 256; /* Parse Arguments */ for (i=1; i 15) || ((id == 0) && (argv[i][0] != '0'))) { fprintf(stderr, "Invalid partition id: %s\n", argv[i]); exit(16); } CMD.partition_id = (uint8_t)id; if (id == 0) CMD.self_update = 1; } else if (strcmp(argv[i], "--sha-only") == 0) { CMD.sha_only = 1; } else if (strcmp(argv[i], "--manual-sign") == 0) { CMD.manual_sign = 1; } else if (strcmp(argv[i], "--encrypt") == 0) { if (CMD.encrypt == ENC_OFF) CMD.encrypt = ENC_CHACHA; CMD.encrypt_key_file = argv[++i]; } else if (strcmp(argv[i], "--aes128") == 0) { CMD.encrypt = ENC_AES128; } else if (strcmp(argv[i], "--aes256") == 0) { CMD.encrypt = ENC_AES256; } else if (strcmp(argv[i], "--chacha") == 0) { CMD.encrypt = ENC_CHACHA; } else if (strcmp(argv[i], "--delta") == 0) { CMD.delta = 1; CMD.delta_base_file = argv[++i]; } else if (strcmp(argv[i], "--no-base-sha") == 0) { CMD.no_base_sha = 1; } else if (strcmp(argv[i], "--no-ts") == 0) { CMD.no_ts = 1; } else if (strcmp(argv[i], "--policy") == 0) { 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 if (strcmp(argv[i], "--custom-tlv-string") == 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]); 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++) { CMD.custom_tlv[p].buffer[j] = (uint8_t)argv[i+2][j]; } CMD.custom_tlvs++; i += 2; } else { i--; break; } } if ((CMD.sign == CMD.secondary_sign) && (CMD.hybrid)) { printf("Warning: Duplicate signature algorithm detected. Fix your command line!\n"); CMD.hybrid = 0; CMD.secondary_key_file = NULL; CMD.secondary_signature_sz = 0; } if (CMD.sign != NO_SIGN) { if (CMD.hybrid) { printf("Parsing arguments in hybrid mode\n"); CMD.image_file = argv[i+1]; CMD.key_file = argv[i+2]; CMD.secondary_key_file = argv[i+3]; CMD.fw_version = argv[i+4]; if (CMD.manual_sign) { CMD.signature_file = argv[i+5]; } printf("Secondary private key: %s\n", CMD.secondary_key_file); printf("Secondary cipher: %s\n", secondary_sign_str); printf("Version: %s\n", CMD.fw_version); } else { CMD.image_file = argv[i+1]; CMD.key_file = argv[i+2]; CMD.fw_version = argv[i+3]; if (CMD.manual_sign) { CMD.signature_file = argv[i+4]; } } } else { CMD.image_file = argv[i+1]; CMD.key_file = NULL; CMD.fw_version = argv[i+2]; } memset(buf, 0, sizeof(buf)); strncpy((char*)buf, CMD.image_file, sizeof(buf)-1); tmpstr = strrchr((char*)buf, '.'); if (tmpstr) { *tmpstr = '\0'; /* null terminate at last "." */ } snprintf(CMD.output_image_file, sizeof(CMD.output_image_file) - 1, "%s_v%s_%s.bin", (char*)buf, CMD.fw_version, CMD.sha_only ? "digest" : "signed"); snprintf(CMD.output_encrypted_image_file, sizeof(CMD.output_encrypted_image_file), "%s_v%s_signed_and_encrypted.bin", (char*)buf, CMD.fw_version); printf("Update type: %s\n", CMD.self_update ? "wolfBoot" : "Firmware"); switch(CMD.encrypt) { case ENC_OFF: break; case ENC_CHACHA: printf("Encryption Algorithm: ChaCha20\n"); break; case ENC_AES128: printf("Encryption Algorithm: AES128-CTR\n"); break; case ENC_AES256: printf("Encryption Algorithm: AES256-CTR\n"); break; } printf("Input image: %s\n", CMD.image_file); printf("Selected cipher: %s\n", sign_str); printf("Selected hash : %s\n", hash_str); if (CMD.sign != NO_SIGN) { printf("Private key: %s\n", CMD.key_file); } if (CMD.hybrid) { printf("Secondary cipher: %s\n", secondary_sign_str); printf("Secondary private key: %s\n", CMD.secondary_key_file); } if (CMD.delta) { printf("Delta Base file: %s\n", CMD.delta_base_file); snprintf(CMD.output_diff_file, sizeof(CMD.output_image_file), "%s_v%s_signed_diff.bin", (char*)buf, CMD.fw_version); snprintf(CMD.output_encrypted_image_file, sizeof(CMD.output_encrypted_image_file), "%s_v%s_signed_diff_encrypted.bin", (char*)buf, CMD.fw_version); } printf("Output %6s: %s\n", CMD.sha_only ? "digest" : "image", CMD.output_image_file); if (CMD.encrypt) { printf("Encrypted output: %s\n", CMD.output_encrypted_image_file); } printf("Target partition id : %hu ", CMD.partition_id); if (CMD.partition_id == HDR_IMG_TYPE_WOLFBOOT) 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"); } } set_signature_sizes(0); if (CMD.hybrid) { set_signature_sizes(1); } if (((CMD.sign != NO_SIGN) && (CMD.signature_sz == 0)) || CMD.header_sz == 0) { printf("Invalid hash or signature type! %d, %d, %d\n", CMD.sign, CMD.signature_sz, CMD.header_sz); exit(2); } if (CMD.sign == NO_SIGN) { printf ("*** WARNING: cipher 'none' selected.\n" "*** Image will not be authenticated!\n" "*** SECURE BOOT DISABLED.\n"); } else { kbuf = load_key(&key_buffer, &key_buffer_sz, &pubkey, &pubkey_sz, 0); if (!kbuf) { exit(1); } } /* CMD.sign != NO_SIGN */ if (CMD.hybrid) { uint8_t *kbuf2 = NULL; uint8_t *pubkey2 = NULL; uint32_t pubkey_sz2; DEBUG_PRINT("Loading secondary key\n"); kbuf2 = load_key(&key_buffer2, &key_buffer_sz2, &pubkey2, &pubkey_sz2, 1); printf("Creating hybrid signature\n"); make_hybrid_header(pubkey, pubkey_sz, CMD.image_file, CMD.output_image_file, pubkey2, pubkey_sz2); DEBUG_PRINT("Signature size: %u\n", CMD.signature_sz); DEBUG_PRINT("Secondary signature size: %u\n", CMD.secondary_signature_sz); DEBUG_PRINT("Header size: %u\n", CMD.header_sz); if (kbuf2) free(kbuf2); } else { make_header(pubkey, pubkey_sz, CMD.image_file, CMD.output_image_file); } if (CMD.delta) { if (CMD.encrypt) ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 64); else ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 16); } if (kbuf) free(kbuf); if (CMD.sign == SIGN_ED25519) { wc_ed25519_free(&key.ed); } else if (CMD.sign == SIGN_ED448) { wc_ed448_free(&key.ed4); } else if (CMD.sign == SIGN_ECC256 || CMD.sign == SIGN_ECC384 || CMD.sign == SIGN_ECC521) { wc_ecc_free(&key.ecc); } else if (CMD.sign == SIGN_RSA2048 || CMD.sign == SIGN_RSA3072 || CMD.sign == SIGN_RSA4096) { wc_FreeRsaKey(&key.rsa); } else if (CMD.sign == SIGN_LMS) { wc_LmsKey_Free(&key.lms); } else if (CMD.sign == SIGN_XMSS) { wc_XmssKey_Free(&key.xmss); } else if (CMD.sign == SIGN_ML_DSA) { wc_MlDsaKey_Free(&key.ml_dsa); } return ret; }