diff --git a/autogen.sh b/autogen.sh index 32ea6f6..d8c4b5e 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,6 +1,6 @@ #!/bin/bash -ccan_modules="talloc read_write_all build_assert array_size" +ccan_modules="talloc read_write_all build_assert array_size endian" # Add ccan upstream sources if [ ! -e lib/ccan.git/Makefile ] diff --git a/src/image.c b/src/image.c index 9fdeecd..6f6e8b0 100644 --- a/src/image.c +++ b/src/image.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -134,6 +135,62 @@ static int align_up(int size, int align) return (size + align - 1) & ~(align - 1); } +static uint16_t csum_update_fold(uint16_t csum, uint16_t x) +{ + uint32_t new = csum + x; + new = (new >> 16) + (new & 0xffff); + return new; +} + +static uint16_t csum_bytes(uint16_t checksum, void *buf, size_t len) +{ + unsigned int i; + uint16_t *p; + + for (i = 0; i < len; i += sizeof(*p)) { + p = buf + i; + checksum = csum_update_fold(checksum, *p); + } + + return checksum; +} + +static void image_pecoff_update_checksum(struct image *image) +{ + bool is_signed = image->sigsize && image->sigbuf; + uint32_t checksum; + struct cert_table_header *cert_table = image->cert_table; + + /* We carefully only include the signature data in the checksum (and + * in the file length) if we're outputting the signature. Otherwise, + * in case of signature removal, the signature data is in the buffer + * we read in (as indicated by image->size), but we do *not* want to + * checksum it. + * + * We also skip the 32-bits of checksum data in the PE/COFF header. + */ + checksum = csum_bytes(0, image->buf, + (void *)image->checksum - (void *)image->buf); + checksum = csum_bytes(checksum, + image->checksum + 1, + (void *)(image->buf + image->data_size) - + (void *)(image->checksum + 1)); + + if (is_signed) { + checksum = csum_bytes(checksum, + cert_table, sizeof(*cert_table)); + + checksum = csum_bytes(checksum, image->sigbuf, image->sigsize); + } + + checksum += image->data_size; + + if (is_signed) + checksum += sizeof(*cert_table) + image->sigsize; + + *(image->checksum) = cpu_to_le32(checksum); +} + static int image_pecoff_parse(struct image *image) { struct cert_table_header *cert_table; @@ -508,6 +565,8 @@ int image_add_signature(struct image *image, void *sig, int size) if (aligned_size != tot_size) memset(start + size, 0, aligned_size - tot_size); + image->cert_table = cth; + return 0; } @@ -587,6 +646,8 @@ int image_write(struct image *image, const char *filename) image->data_dir_sigtable->size = 0; } + image_pecoff_update_checksum(image); + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror("open");