Commit f3be9e9d authored by Marek Vasut's avatar Marek Vasut
Browse files

mxssb: Implement basic image verification



By using the -i option of MXSSB, one can now validate if the input
image is correct. MXSSB will also dump the contents of the image.
Signed-off-by: Marek Vasut's avatarMarek Vasut <marex@denx.de>
parent 200e8d62
......@@ -12,8 +12,8 @@ Build:
2) Compile the tool by running simple "make" command . The produced binary
will be called "mxssb" and will reside in the current directory.
Use:
====
Usage -- producing image:
=========================
The MXSSB tool is targetted to be a simple replacement for the elftosb2 .
To generate an image, simply write an image configuration file and run:
......@@ -109,3 +109,15 @@ These semantics and rules will be outlined now.
J -- JUMP instruction
C -- CALL instruction
M -- MODE instruction
Usage -- verifying image:
=========================
The MXSSB can also verify and dump contents of an image. Use the following
syntax to verify and dump contents of an image:
mxssb -i <input bootstream file>
This will output all the information from the SB image header and all the
instructions contained in the SB image. It will also check if the various
checksums in the SB image are correct.
......@@ -211,6 +211,19 @@ static time_t sb_get_timestamp(void)
return seconds_to_now - seconds_to_2000;
}
static int sb_get_time(time_t time, struct tm *tm)
{
struct tm time_2000 = {
.tm_yday = 1, /* Jan. 1st */
.tm_year = 0, /* 1900 */
};
const time_t seconds_to_2000 = mktime(&time_2000);
const time_t seconds_to_now = seconds_to_2000 + time;
struct tm *ret;
ret = gmtime_r(&seconds_to_now, tm);
return ret ? 0 : -EINVAL;
}
static void sb_encrypt_sb_header(struct sb_image_ctx *ictx)
{
EVP_MD_CTX *md_ctx = &ictx->md_ctx;
......@@ -1309,6 +1322,440 @@ static int sb_build_tree_from_cfg(struct sb_image_ctx *ictx)
return 0;
}
static int sb_verify_image_header(struct sb_image_ctx *ictx,
int fd, off_t fsize)
{
/* Verify static fields in the image header. */
struct sb_boot_image_header *hdr = &ictx->payload;
const char *stat[2] = { "[PASS]", "[FAIL]" };
struct tm tm;
int sz, ret = 0;
unsigned char digest[20];
EVP_MD_CTX md_ctx;
ssize_t size;
/* Start image-wide crypto. */
EVP_MD_CTX_init(&ictx->md_ctx);
EVP_DigestInit(&ictx->md_ctx, EVP_sha1());
fprintf(stdout, "---------- Verifying SB Image Header ----------\n");
size = read(fd, &ictx->payload, sizeof(ictx->payload));
if (size != sizeof(ictx->payload)) {
fprintf(stderr, "ERR: SB image header too short!\n");
return -EINVAL;
}
/* Compute header digest. */
EVP_MD_CTX_init(&md_ctx);
EVP_DigestInit(&md_ctx, EVP_sha1());
EVP_DigestUpdate(&md_ctx, hdr->signature1,
sizeof(struct sb_boot_image_header) -
sizeof(hdr->digest));
EVP_DigestFinal(&md_ctx, digest, NULL);
sb_aes_init(ictx, NULL, 1);
sb_encrypt_sb_header(ictx);
if (memcmp(digest, hdr->digest, 20))
ret = -EINVAL;
fprintf(stdout, "%s Image header checksum: %s\n", stat[!!ret],
ret ? "BAD" : "OK");
if (ret)
return ret;
if (memcmp(hdr->signature1, "STMP", 4) ||
memcmp(hdr->signature2, "sgtl", 4))
ret = -EINVAL;
fprintf(stdout, "%s Signatures: '%.4s' '%.4s'\n",
stat[!!ret], hdr->signature1, hdr->signature2);
if (ret)
return ret;
if ((hdr->major_version != SB_VERSION_MAJOR) ||
((hdr->minor_version != 1) && (hdr->minor_version != 2)))
ret = -EINVAL;
fprintf(stdout, "%s Image version: v%i.%i\n", stat[!!ret],
hdr->major_version, hdr->minor_version);
if (ret)
return ret;
ret = sb_get_time(hdr->timestamp_us / 1000000, &tm);
fprintf(stdout, "%s Creation time: "
"%02i:%02i:%02i %02i/%02i/%04i\n",
stat[!!ret], tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_mday, tm.tm_mon, tm.tm_year + 2000);
if (ret)
return ret;
fprintf(stdout, "%s Product version: %x.%x.%x\n", stat[0],
__be16_to_cpu(hdr->product_version.major),
__be16_to_cpu(hdr->product_version.minor),
__be16_to_cpu(hdr->product_version.revision));
fprintf(stdout, "%s Component version: %x.%x.%x\n", stat[0],
__be16_to_cpu(hdr->component_version.major),
__be16_to_cpu(hdr->component_version.minor),
__be16_to_cpu(hdr->component_version.revision));
if (hdr->flags & ~SB_IMAGE_FLAG_VERBOSE)
ret = -EINVAL;
fprintf(stdout, "%s Image flags: %s\n", stat[!!ret],
hdr->flags & SB_IMAGE_FLAG_VERBOSE ? "Verbose_boot" : "");
if (ret)
return ret;
if (hdr->drive_tag != 0)
ret = -EINVAL;
fprintf(stdout, "%s Drive tag: %i\n", stat[!!ret],
hdr->drive_tag);
if (ret)
return ret;
sz = sizeof(struct sb_boot_image_header) / SB_BLOCK_SIZE;
if (hdr->header_blocks != sz)
ret = -EINVAL;
fprintf(stdout, "%s Image header size (blocks): %i\n", stat[!!ret],
hdr->header_blocks);
if (ret)
return ret;
sz = sizeof(struct sb_sections_header) / SB_BLOCK_SIZE;
if (hdr->section_header_size != sz)
ret = -EINVAL;
fprintf(stdout, "%s Section header size (blocks): %i\n", stat[!!ret],
hdr->section_header_size);
if (ret)
return ret;
fprintf(stdout, "%s Sections count: %i\n", stat[!!ret],
hdr->section_count);
fprintf(stdout, "%s First bootable section %i\n", stat[!!ret],
hdr->first_boot_section_id);
if (hdr->image_blocks != fsize / SB_BLOCK_SIZE)
ret = -EINVAL;
fprintf(stdout, "%s Image size (blocks): %i\n", stat[!!ret],
hdr->image_blocks);
if (ret)
return ret;
sz = hdr->header_blocks + hdr->section_header_size * hdr->section_count;
if (hdr->key_dictionary_block != sz)
ret = -EINVAL;
fprintf(stdout, "%s Key dict offset (blocks): %i\n", stat[!!ret],
hdr->key_dictionary_block);
if (ret)
return ret;
if (hdr->key_count != 1)
ret = -EINVAL;
fprintf(stdout, "%s Number of encryption keys: %i\n", stat[!!ret],
hdr->key_count);
if (ret)
return ret;
sz = hdr->header_blocks + hdr->section_header_size * hdr->section_count;
sz += hdr->key_count * sizeof(struct sb_key_dictionary_key) / SB_BLOCK_SIZE;
if (hdr->first_boot_tag_block != (unsigned)sz)
ret = -EINVAL;
fprintf(stdout, "%s First TAG block (blocks): %i\n", stat[!!ret],
hdr->first_boot_tag_block);
if (ret)
return ret;
return 0;
}
static void sb_decrypt_tag(struct sb_image_ctx *ictx,
struct sb_cmd_ctx *cctx)
{
EVP_MD_CTX *md_ctx = &ictx->md_ctx;
struct sb_command *cmd = &cctx->payload;
sb_aes_crypt(ictx, (uint8_t *)&cctx->c_payload,
(uint8_t *)&cctx->payload, sizeof(*cmd));
EVP_DigestUpdate(md_ctx, &cctx->c_payload, sizeof(*cmd));
}
static int sb_verify_command(struct sb_image_ctx *ictx,
struct sb_cmd_ctx *cctx, int fd, ssize_t *tsize)
{
struct sb_command *ccmd = &cctx->payload;
ssize_t size;
char *csum, *flag = "";
int ret;
unsigned int i;
uint8_t csn, csc = ccmd->header.checksum;
ccmd->header.checksum = 0x5a;
csn = sb_command_checksum(ccmd);
ccmd->header.checksum = csc;
if (csc == csn)
ret = 0;
else
ret = -EINVAL;
csum = ret ? "checksum BAD" : "checksum OK";
switch (ccmd->header.tag) {
case ROM_NOP_CMD:
fprintf(stdout, " NOOP # %s\n", csum);
return ret;
case ROM_TAG_CMD:
if (ccmd->header.flags & ROM_TAG_CMD_FLAG_ROM_LAST_TAG)
flag = "LAST";
fprintf(stdout, " TAG %s # %s\n", flag, csum);
sb_aes_reinit(ictx, 0);
return ret;
case ROM_LOAD_CMD:
fprintf(stdout, " LOAD addr=0x%08x length=0x%08x # %s\n",
ccmd->load.address, ccmd->load.count, csum);
cctx->length = ccmd->load.count;
cctx->data = malloc(cctx->length);
if (!cctx->data)
return -ENOMEM;
size = read(fd, cctx->data, cctx->length);
if (size != cctx->length) {
fprintf(stderr,
"ERR: SB LOAD command payload too short!\n");
return -EINVAL;
}
*tsize += size;
sb_aes_crypt(ictx, cctx->data, cctx->data, cctx->length);
EVP_DigestUpdate(&ictx->md_ctx, cctx->data, cctx->length);
if (ccmd->load.crc32 != crc32(cctx->data, cctx->length)) {
fprintf(stderr,
"ERR: SB LOAD command payload CRC32 invalid!\n");
return -EINVAL;
}
return 0;
case ROM_FILL_CMD:
fprintf(stdout,
" FILL addr=0x%08x length=0x%08x pattern=0x%08x # %s\n",
ccmd->fill.address, ccmd->fill.count,
ccmd->fill.pattern, csum);
return 0;
case ROM_JUMP_CMD:
if (ccmd->header.flags & ROM_JUMP_CMD_FLAG_HAB)
flag = "HAB";
fprintf(stdout,
" JUMP %s addr=0x%08x r0_arg=0x%08x # %s\n",
flag, ccmd->fill.address, ccmd->jump.argument, csum);
return 0;
case ROM_CALL_CMD:
if (ccmd->header.flags & ROM_CALL_CMD_FLAG_HAB)
flag = "HAB";
fprintf(stdout,
" CALL %s addr=0x%08x r0_arg=0x%08x # %s\n",
flag, ccmd->fill.address, ccmd->jump.argument, csum);
return 0;
case ROM_MODE_CMD:
for (i = 0; i < sizeof(modetable)/sizeof(modetable[0]); i++) {
if (ccmd->mode.mode == modetable[i].mode) {
fprintf(stdout, " MODE %s # %s\n",
modetable[i].name, csum);
break;
}
}
fprintf(stderr, " MODE !INVALID! # %s\n", csum);
return 0;
}
return ret;
}
static int sb_verify_commands(struct sb_image_ctx *ictx,
struct sb_section_ctx *sctx, int fd)
{
ssize_t size, tsize = 0;
struct sb_cmd_ctx *cctx;
int ret;
sb_aes_reinit(ictx, 0);
while (tsize < sctx->size) {
cctx = calloc(1, sizeof(*cctx));
if (!cctx)
return -ENOMEM;
if (!sctx->cmd_head) {
sctx->cmd_head = cctx;
sctx->cmd_tail = cctx;
} else {
sctx->cmd_tail->cmd = cctx;
sctx->cmd_tail = cctx;
}
size = read(fd, &cctx->c_payload, sizeof(cctx->c_payload));
if (size != sizeof(cctx->c_payload)) {
fprintf(stderr, "ERR: SB command header too short!\n");
return -EINVAL;
}
tsize += size;
sb_decrypt_tag(ictx, cctx);
ret = sb_verify_command(ictx, cctx, fd, &tsize);
if (ret)
return -EINVAL;
}
return 0;
}
static int sb_verify_sections_cmds(struct sb_image_ctx *ictx, int fd)
{
struct sb_boot_image_header *hdr = &ictx->payload;
struct sb_sections_header *shdr;
unsigned int i;
int ret;
struct sb_section_ctx *sctx;
ssize_t size;
char *bootable;
fprintf(stdout, "----- Verifying SB Sections and Commands -----\n");
for (i = 0; i < hdr->section_count; i++) {
sctx = calloc(1, sizeof(*sctx));
if (!sctx)
return -ENOMEM;
if (!ictx->sect_head) {
ictx->sect_head = sctx;
ictx->sect_tail = sctx;
} else {
ictx->sect_tail->sect = sctx;
ictx->sect_tail = sctx;
}
size = read(fd, &sctx->payload, sizeof(sctx->payload));
if (size != sizeof(sctx->payload)) {
fprintf(stderr, "ERR: SB section header too short!\n");
return -EINVAL;
}
}
uint8_t sb_section_header_cbc_mac[sizeof(struct sb_key_dictionary_key)];
struct sb_key_dictionary_key key;
size = read(fd, &key, sizeof(key));
if (size != sizeof(key)) {
fprintf(stderr, "ERR: SB key dictionary too short!\n");
return -EINVAL;
}
sb_encrypt_sb_sections_header(ictx, sb_section_header_cbc_mac);
sb_aes_reinit(ictx, 1);
sb_encrypt_key_dictionary_key(ictx, sb_section_header_cbc_mac);
if (memcmp(&key, &ictx->sb_dict_key, sizeof(key))) {
fprintf(stderr,
"ERR: Key dict entry for section header is invalid!\n");
return -EINVAL;
}
sb_aes_reinit(ictx, 0);
sctx = ictx->sect_head;
while (sctx) {
shdr = &sctx->payload;
if (shdr->section_flags & SB_SECTION_FLAG_BOOTABLE)
sctx->boot = 1;
bootable = sctx->boot ? "BOOTABLE" : "";
sctx->size = (shdr->section_size * SB_BLOCK_SIZE) +
sizeof(struct sb_command);
fprintf(stdout, "SECTION 0x%x %s # size = %i bytes\n",
shdr->section_number, bootable, sctx->size);
if (shdr->section_flags & ~SB_SECTION_FLAG_BOOTABLE)
fprintf(stderr, " WARN: Unknown section flag(s) %08x\n",
shdr->section_flags);
if ((shdr->section_flags & SB_SECTION_FLAG_BOOTABLE) &&
(hdr->first_boot_section_id != shdr->section_number))
fprintf(stderr, " WARN: Bootable section does ID not match image header ID!\n");
ret = sb_verify_commands(ictx, sctx, fd);
if (ret)
goto exit;
sctx = sctx->sect;
}
/*
* FIXME IDEA:
* check if the first TAG command is at sctx->section_offset
*/
exit:
sb_aes_reinit(ictx, 1);
return 0;
}
char *input_filename;
static int sb_build_tree_from_img(struct sb_image_ctx *ictx)
{
const char *filename = input_filename;
off_t filesize;
int fd, ret;
if (!filename) {
fprintf(stderr, "ERR: Missing filename!\n");
return -EINVAL;
}
fd = open(filename, O_RDONLY);
if (fd < 0)
goto err_open;
filesize = lseek(fd, 0, SEEK_END);
if (filesize < 0)
goto err_file;
lseek(fd, 0, SEEK_SET);
if (filesize < (signed)sizeof(ictx->payload)) {
fprintf(stderr, "ERR: File too short!\n");
goto err_file;
}
/* Load and verify image header */
ret = sb_verify_image_header(ictx, fd, filesize);
if (ret)
goto err_verify;
/* Load and verify sections and commands */
ret = sb_verify_sections_cmds(ictx, fd);
if (ret)
goto err_verify;
ret = 0;
err_verify:
fprintf(stdout, "-------------------- Result -------------------\n");
fprintf(stdout, "Verification %s\n", ret ? "FAILED" : "PASSED");
/*
* FIXME IDEA -- check the whole-image digest
*/
/* Stop the encryption session. */
sb_aes_deinit(&ictx->cipher_ctx);
close(fd);
return ret;
err_file:
close(fd);
err_open:
fprintf(stderr, "ERR: Failed to load file \"%s\"\n", filename);
return -EINVAL;
}
static void sb_free_image(struct sb_image_ctx *ictx)
{
struct sb_section_ctx *sctx = ictx->sect_head, *s_head;
......@@ -1334,11 +1781,13 @@ static void sb_free_image(struct sb_image_ctx *ictx)
static void print_help(const char *pn)
{
printf(
"Usage: %s -c FILE -o FILE [-v] [-h]\n\n"
"Usage: %s -c FILE -o FILE [-v] [-h]\n"
" %s -i FILE [-h]\n"
" -c FILE ..... path to command file (mxsimage.cfg)\n"
" -o FILE ..... path to output SB file (u-boot.sb)\n"
" -i FILE ..... path to input SB file (u-boot.sb)\n"
" -v .......... verbose boot output from BootROM\n"
" -h .......... print help\n", pn);
" -h .......... print help\n", pn, pn);
}
int main(int argc, char **argv)
......@@ -1349,7 +1798,7 @@ int main(int argc, char **argv)
struct sb_image_ctx ctx;
memset(&ctx, 0, sizeof(ctx));
while ((opt = getopt(argc, argv, "c:o:vh")) != -1) {
while ((opt = getopt(argc, argv, "c:o:i:vh")) != -1) {
switch (opt) {
case 'c':
ctx.cfg_filename = optarg;
......@@ -1357,6 +1806,9 @@ int main(int argc, char **argv)
case 'o':
output_filename = optarg;
break;
case 'i':
input_filename = optarg;
break;
case 'v':
ctx.verbose_boot = 1;
break;
......@@ -1364,31 +1816,39 @@ int main(int argc, char **argv)
print_help(argv[0]);
return -EINVAL;
default:
fprintf(stderr, "ERROR: Invalid option '%c'.\n\n", opt);
fprintf(stderr, "ERR: Invalid option '%c'.\n\n", opt);
print_help(argv[0]);
return -EINVAL;
}
}
if (optind != argc) {
fprintf(stderr, "ERROR: Expected argument after options!\n\n");
fprintf(stderr, "ERR: Expected argument after options!\n\n");
print_help(argv[0]);
return -EINVAL;
}
if (!ctx.cfg_filename || !output_filename) {
fprintf(stderr, "ERROR: Input files not specified!\n\n");
if (!(input_filename || (ctx.cfg_filename && output_filename)) ||
(input_filename && (ctx.cfg_filename && output_filename))) {
fprintf(stderr, "ERR: Please specify either input filename or\n"
" configuration file and output filename!\n");
print_help(argv[0]);
return -EINVAL;
}
ret = sb_build_tree_from_cfg(&ctx);
if (ret)
goto fail;
ret = sb_encrypt_image(&ctx);
if (!ret)
ret = sb_write_image(&ctx);
if (input_filename) {
/* Image verification and dump. */
ret = sb_build_tree_from_img(&ctx);
} else if (ctx.cfg_filename && output_filename) {
/* Image creation */
ret = sb_build_tree_from_cfg(&ctx);
if (ret)
goto fail;
ret = sb_encrypt_image(&ctx);
if (!ret)
ret = sb_write_image(&ctx);
}
fail:
sb_free_image(&ctx);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment