Changed delta mechanism + added key tool diff function

pull/125/head
Daniele Lacamera 2021-06-17 10:55:25 +02:00
parent 6fae229a63
commit b74a4f69e2
6 changed files with 247 additions and 212 deletions

View File

@ -55,7 +55,6 @@
#define HDR_SHA256 0x03
#define HDR_IMG_TYPE 0x04
#define HDR_IMG_DELTA_BASE 0x05
#define HDR_PREDIFF_MANIFEST 0x40
#define HDR_PUBKEY 0x10
#define HDR_SIGNATURE 0x20
#define HDR_SHA3_384 0x13

View File

@ -26,6 +26,10 @@ struct __attribute__((packed)) block_hdr {
#define BLOCK_HDR_SIZE (sizeof (struct block_hdr))
#define MAX_SRC_SIZE (1 << 24)
#ifndef WOLFBOOT_SECTOR_SIZE
# define WOLFBOOT_SECTOR_SIZE 0x1000
#endif
int wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, uint8_t *patch, uint32_t psz)
{
@ -40,10 +44,6 @@ int wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, uint8_t *patch,
return 0;
}
#ifndef WOLFBOOT_BLOCK_SIZE
# define WOLFBOOT_BLOCK_SIZE 0x100
#endif
int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len)
{
struct block_hdr *hdr;
@ -130,11 +130,12 @@ int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len)
if (len < BLOCK_HDR_SIZE)
return -1;
while ((ctx->off_b < ctx->size_b) && (len > p_off + BLOCK_HDR_SIZE)) {
pa = ctx->src_a;
uint32_t page_start = ctx->off_b / WOLFBOOT_SECTOR_SIZE;
pa = ctx->src_a + page_start;
found = 0;
if (p_off + BLOCK_HDR_SIZE > len)
return p_off;
while (((uint32_t)(pa - ctx->src_a) < (ctx->size_a - BLOCK_HDR_SIZE)) && (p_off < len)) {
while (((uint32_t)(pa - ctx->src_a) < ctx->size_a) && (p_off < len)) {
if ((ctx->size_b - ctx->off_b) < BLOCK_HDR_SIZE)
break;
if ((memcmp(pa, (ctx->src_b + ctx->off_b), BLOCK_HDR_SIZE) == 0)) {

View File

@ -519,43 +519,6 @@ uint32_t wolfBoot_get_diffbase_version(uint8_t part)
return wolfBoot_get_blob_diffbase_version(image);
}
int wolfBoot_get_diffbase_hdr(uint8_t part, uint8_t **ptr)
{
uint8_t *image = NULL;
uint32_t *magic = NULL;
uint16_t ret;
if(part == PART_UPDATE) {
if (PARTN_IS_EXT(PART_UPDATE))
{
#ifdef EXT_FLASH
ext_flash_check_read((uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, hdr_cpy, IMAGE_HEADER_SIZE);
hdr_cpy_done = 1;
image = hdr_cpy;
#endif
} else {
image = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
}
} else if (part == PART_BOOT) {
if (PARTN_IS_EXT(PART_BOOT)) {
#ifdef EXT_FLASH
ext_flash_check_read((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS, hdr_cpy, IMAGE_HEADER_SIZE);
hdr_cpy_done = 1;
image = hdr_cpy;
#endif
} else {
image = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
}
}
magic = (uint32_t *)image;
if (*magic != WOLFBOOT_MAGIC)
return -1;
ret = wolfBoot_find_header(image + IMAGE_HEADER_OFFSET, HDR_PREDIFF_MANIFEST, ptr);
if (ret == 0)
return -1;
else
return (int)ret;
}
uint16_t wolfBoot_get_image_type(uint8_t part)
{
uint16_t *type_field = NULL;

View File

@ -158,37 +158,25 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, struct wolfBoot_im
int ret;
uint8_t flag;
int hdr_size;
uint8_t *manifest_hdr;
/* Read encryption key/IV before starting the update */
#ifdef EXT_ENCRYPTED
wolfBoot_get_encrypt_key(key, nonce);
#endif
WB_PATCH_CTX ctx;
/* Check that image contains the header with the manifest before delta */
hdr_size = wolfBoot_get_diffbase_hdr(PART_UPDATE, &manifest_hdr);
if (hdr_size < 0)
return -1;
ret = wb_patch_init(&ctx, boot->fw_base + IMAGE_HEADER_SIZE, boot->fw_size,
ret = wb_patch_init(&ctx, boot->fw_base, boot->fw_size + IMAGE_HEADER_SIZE,
update->fw_base + IMAGE_HEADER_SIZE, update->fw_size);
if (ret < 0)
return ret;
while((sector * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE) {
if ((wolfBoot_get_update_sector_flag(sector, &flag) != 0) || (flag == SECT_FLAG_NEW)) {
flag = SECT_FLAG_SWAPPING;
wolfBoot_copy_sector(boot, swap, sector);
if (((sector + 1) * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE)
wolfBoot_set_update_sector_flag(sector, flag);
}
if (flag == SECT_FLAG_SWAPPING) {
int len = 0;
wb_flash_erase(boot, sector * WOLFBOOT_SECTOR_SIZE, WOLFBOOT_SECTOR_SIZE);
wb_flash_erase(swap, 0, WOLFBOOT_SECTOR_SIZE);
while (len < WOLFBOOT_SECTOR_SIZE) {
ret = wb_patch(&ctx, delta_blk, WOLFBOOT_SECTOR_SIZE);
if (ret > 0) {
wb_flash_write(boot, sector * WOLFBOOT_SECTOR_SIZE, delta_blk + len, ret);
wb_flash_write(swap, 0, delta_blk + len, ret);
len += ret;
} else if (ret == 0) {
flag = SECT_FLAG_UPDATED;
@ -197,17 +185,20 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, struct wolfBoot_im
} else
return -1;
}
flag = SECT_FLAG_UPDATED;
flag = SECT_FLAG_SWAPPING;
wolfBoot_set_update_sector_flag(sector, flag);
}
if (flag == SECT_FLAG_SWAPPING) {
wolfBoot_copy_sector(swap, boot, sector);
flag = SECT_FLAG_UPDATED;
if (((sector + 1) * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE)
wolfBoot_set_update_sector_flag(sector, flag);
}
sector++;
}
/* Put pre-diff manifest header for the new firmware */
wb_flash_write(boot, 0, manifest_hdr, hdr_size);
return 0;
}
#endif
static int wolfBoot_update(int fallback_allowed)

View File

@ -21,7 +21,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
'''
import sys, os, struct, time
import sys, os, struct, time, re
from wolfcrypt import ciphers, hashes
@ -49,9 +49,11 @@ HDR_IMG_TYPE_AUTH_ED25519 = 0x0100
HDR_IMG_TYPE_AUTH_ECC256 = 0x0200
HDR_IMG_TYPE_AUTH_RSA2048 = 0x0300
HDR_IMG_TYPE_AUTH_RSA4096 = 0x0400
HDR_IMG_TYPE_DIFF = 0x00D0
HDR_IMG_TYPE_WOLFBOOT = 0x0000
HDR_IMG_TYPE_APP = 0x0001
HDR_IMG_DELTA_BASE = 0x0005
WOLFBOOT_HEADER_SIZE = 256
@ -60,18 +62,179 @@ self_update=False
sha_only=False
manual_sign=False
encrypt=False
delta=False
encrypt_key_file=None
delta_base_file=None
argc = len(sys.argv)
argv = sys.argv
hash_algo='sha256'
def make_header(image_file, extra_fields=[]):
img_size = os.path.getsize(image_file)
# Magic header (spells 'WOLF')
header = struct.pack('<L', WOLFBOOT_MAGIC)
# Image size
header += struct.pack('<L', img_size)
# No pad bytes, version is aligned
# Version field
header += struct.pack('<HH', HDR_VERSION, HDR_VERSION_LEN)
header += struct.pack('<L', fw_version)
# Four pad bytes, so timestamp is aligned
header += struct.pack('BB', 0xFF, 0xFF)
header += struct.pack('BB', 0xFF, 0xFF)
# Timestamp field
header += struct.pack('<HH', HDR_TIMESTAMP, HDR_TIMESTAMP_LEN)
header += struct.pack('<Q', int(os.path.getmtime(image_file)))
# Image type field
header += struct.pack('<HH', HDR_IMG_TYPE, HDR_IMG_TYPE_LEN)
if (sign == 'none'):
img_type = HDR_IMG_TYPE_AUTH_NONE
if (sign == 'ed25519'):
img_type = HDR_IMG_TYPE_AUTH_ED25519
if (sign == 'ecc256'):
img_type = HDR_IMG_TYPE_AUTH_ECC256
if (sign == 'rsa2048'):
img_type = HDR_IMG_TYPE_AUTH_RSA2048
if (sign == 'rsa4096'):
img_type = HDR_IMG_TYPE_AUTH_RSA4096
if (not self_update):
img_type |= HDR_IMG_TYPE_APP
if (delta and len(extra_fields) > 0):
img_type |= HDR_IMG_TYPE_DIFF
header += struct.pack('<H', img_type)
for t in extra_fields:
tag = t[0]
sz = t[1]
payload = t[2]
header += struct.pack('<HH', tag, sz)
header += payload
# Pad bytes. Sha-3 field requires 8-byte alignment
while (len(header) % 8) != 4:
header += struct.pack('B', 0xFF)
print("Calculating %s digest..." % hash_algo)
if hash_algo == 'sha256':
sha = hashes.Sha256.new()
# Sha calculation
sha.update(header)
img_bin = open(image_file, 'rb')
while True:
buf = img_bin.read(32)
if (len(buf) == 0):
img_bin.close()
break
sha.update(buf)
digest = sha.digest()
# Add SHA to the header
header += struct.pack('<HH', HDR_SHA256, HDR_SHA256_LEN)
header += digest
if sign != 'none':
# pubkey SHA calculation
keysha = hashes.Sha256.new()
keysha.update(pubkey)
key_digest = keysha.digest()
header += struct.pack('<HH', HDR_PUBKEY, HDR_SHA256_LEN)
header += key_digest
elif hash_algo == 'sha3':
sha = hashes.Sha3.new()
# Sha calculation
sha.update(header)
img_bin = open(image_file, 'rb')
while True:
buf = img_bin.read(128)
if (len(buf) == 0):
img_bin.close()
break
sha.update(buf)
digest = sha.digest()
# Add SHA to the header
header += struct.pack('<HH', HDR_SHA3_384, HDR_SHA3_384_LEN)
header += digest
if sign != 'none':
# pubkey SHA calculation
keysha = hashes.Sha3.new()
keysha.update(pubkey)
key_digest = keysha.digest()
header += struct.pack('<HH', HDR_PUBKEY, HDR_SHA3_384_LEN)
header += key_digest
#print("Image Hash %d" % len(digest))
#print([hex(j) for j in digest])
#print ("Pubkey: %d" % len(pubkey))
#print([hex(j) for j in pubkey])
#print("Pubkey Hash %d" % len(key_digest))
#print([hex(j) for j in key_digest])
if sha_only:
outfile = open(output_image_file, 'wb')
outfile.write(digest)
outfile.close()
print("Digest image " + output_image_file +" successfully created.")
print()
sys.exit(0)
if sign != 'none':
# Sign the digest
if not manual_sign:
print("Signing the firmware...")
if (sign == 'ed25519'):
signature = ed.sign(digest)
elif (sign == 'ecc256'):
r, s = ecc.sign_raw(digest)
signature = r + s
elif (sign == 'rsa2048') or (sign == 'rsa4096'):
signature = rsa.sign(digest)
#plain = rsa.verify(signature)
#print("plain:%d " % len(plain))
#print([hex(j) for j in plain])
else:
print("Opening signature file %s" % signature_file)
signfile = open(signature_file, 'rb')
buf = signfile.read(1024)
signfile.close()
if len(buf) != HDR_SIGNATURE_LEN:
print("Wrong signature file size %d, expected %d" % (len(buf), HDR_SIGNATURE_LEN))
sys.exit(4)
signature = buf
header += struct.pack('<HH', HDR_SIGNATURE, HDR_SIGNATURE_LEN)
header += signature
#print ("Signature %d" % len(signature))
#print([hex(j) for j in signature])
print ("Done.")
return header
#### MAIN ####
if (argc < 4) or (argc > 10):
print("Usage: %s [--ed25519 | --ecc256 | --rsa2048 | --rsa4096 | --no-sign ] [--sha256 | --sha3] [--wolfboot-update] [--encrypt key.bin] image key.der fw_version\n" % sys.argv[0])
print("Usage: %s [--ed25519 | --ecc256 | --rsa2048 | --rsa4096 | --no-sign] [--sha256 | --sha3] [--wolfboot-update] [--encrypt key.bin] [--delta base_file.bin] image key.der fw_version\n" % sys.argv[0])
print(" - or - ")
print(" %s [--sha256 | --sha3] [--sha-only] [--wolfboot-update] [--encrypt key.bin] image pub_key.der fw_version\n" % sys.argv[0])
print(" %s [--sha256 | --sha3] [--sha-only] [--wolfboot-update] [--encrypt key.bin] [--delta base_file.bin] image pub_key.der fw_version\n" % sys.argv[0])
print(" - or - ")
print(" %s [--ed25519 | --ecc256 | --rsa2048 | --rsa4096 ] [--sha256 | --sha3] [--manual-sign] [--encrypt key.bin] image pub_key.der fw_version signature.sig\n" % sys.argv[0])
print(" %s [--ed25519 | --ecc256 | --rsa2048 | --rsa4096 ] [--sha256 | --sha3] [--manual-sign] [--encrypt key.bin] [--delta base_file.bin] image pub_key.der fw_version signature.sig\n" % sys.argv[0])
sys.exit(1)
i = 1
@ -100,11 +263,20 @@ while (i < len(argv)):
encrypt = True
i += 1
encrypt_key_file = argv[i]
elif (argv[i] == '--delta'):
delta = True
i += 1
delta_base_file = argv[i]
else:
i-=1
break
i += 1
if (encrypt and delta):
print("Encryption of delta images not supported yet.")
sys.exit(1)
image_file = argv[i+1]
if sign != 'none':
key_file = argv[i+2]
@ -138,6 +310,13 @@ if encrypt:
encrypted_output_image_file += "_v" + str(fw_version) + "_signed_and_encrypted.bin"
else:
encrypted_output_image_file = image_file + "_v" + str(fw_version) + "_signed_and_encrypted.bin"
elif delta:
if '.' in image_file:
tokens = image_file.split('.')
delta_output_image_file = image_file.rstrip('.' + tokens[-1])
delta_output_image_file += "_v" + str(fw_version) + "_signed_diff.bin"
else:
delta_output_image_file = image_file + "_v" + str(fw_version) + "_signed_diff.bin"
if (self_update):
print("Update type: wolfBoot")
@ -253,150 +432,7 @@ else:
pubkey = wolfboot_key_buffer
img_size = os.path.getsize(image_file)
# Magic header (spells 'WOLF')
header = struct.pack('<L', WOLFBOOT_MAGIC)
# Image size
header += struct.pack('<L', img_size)
# No pad bytes, version is aligned
# Version field
header += struct.pack('<HH', HDR_VERSION, HDR_VERSION_LEN)
header += struct.pack('<L', fw_version)
# Four pad bytes, so timestamp is aligned
header += struct.pack('BB', 0xFF, 0xFF)
header += struct.pack('BB', 0xFF, 0xFF)
# Timestamp field
header += struct.pack('<HH', HDR_TIMESTAMP, HDR_TIMESTAMP_LEN)
header += struct.pack('<Q', int(os.path.getmtime(image_file)))
# Image type field
header += struct.pack('<HH', HDR_IMG_TYPE, HDR_IMG_TYPE_LEN)
if (sign == 'none'):
img_type = HDR_IMG_TYPE_AUTH_NONE
if (sign == 'ed25519'):
img_type = HDR_IMG_TYPE_AUTH_ED25519
if (sign == 'ecc256'):
img_type = HDR_IMG_TYPE_AUTH_ECC256
if (sign == 'rsa2048'):
img_type = HDR_IMG_TYPE_AUTH_RSA2048
if (sign == 'rsa4096'):
img_type = HDR_IMG_TYPE_AUTH_RSA4096
if (not self_update):
img_type |= HDR_IMG_TYPE_APP
header += struct.pack('<H', img_type)
# Six pad bytes, Sha-3 requires 8-byte alignment.
header += struct.pack('BB', 0xFF, 0xFF)
header += struct.pack('BB', 0xFF, 0xFF)
header += struct.pack('BB', 0xFF, 0xFF)
print("Calculating %s digest..." % hash_algo)
if hash_algo == 'sha256':
sha = hashes.Sha256.new()
# Sha calculation
sha.update(header)
img_bin = open(image_file, 'rb')
while True:
buf = img_bin.read(32)
if (len(buf) == 0):
img_bin.close()
break
sha.update(buf)
digest = sha.digest()
# Add SHA to the header
header += struct.pack('<HH', HDR_SHA256, HDR_SHA256_LEN)
header += digest
if (sign != 'none'):
# pubkey SHA calculation
keysha = hashes.Sha256.new()
keysha.update(pubkey)
key_digest = keysha.digest()
header += struct.pack('<HH', HDR_PUBKEY, HDR_SHA256_LEN)
header += key_digest
elif hash_algo == 'sha3':
sha = hashes.Sha3.new()
# Sha calculation
sha.update(header)
img_bin = open(image_file, 'rb')
while True:
buf = img_bin.read(128)
if (len(buf) == 0):
img_bin.close()
break
sha.update(buf)
digest = sha.digest()
# Add SHA to the header
header += struct.pack('<HH', HDR_SHA3_384, HDR_SHA3_384_LEN)
header += digest
if (sign != 'none'):
# pubkey SHA calculation
keysha = hashes.Sha3.new()
keysha.update(pubkey)
key_digest = keysha.digest()
header += struct.pack('<HH', HDR_PUBKEY, HDR_SHA3_384_LEN)
header += key_digest
#print("Image Hash %d" % len(digest))
#print([hex(j) for j in digest])
#print ("Pubkey: %d" % len(pubkey))
#print([hex(j) for j in pubkey])
#print("Pubkey Hash %d" % len(key_digest))
#print([hex(j) for j in key_digest])
if sha_only:
outfile = open(output_image_file, 'wb')
outfile.write(digest)
outfile.close()
print("Digest image " + output_image_file +" successfully created.")
print()
sys.exit(0)
# Sign the digest
if not manual_sign:
print("Signing the firmware...")
if (sign == 'ed25519'):
signature = ed.sign(digest)
elif (sign == 'ecc256'):
r, s = ecc.sign_raw(digest)
signature = r + s
elif (sign == 'rsa2048') or (sign == 'rsa4096'):
signature = rsa.sign(digest)
#plain = rsa.verify(signature)
#print("plain:%d " % len(plain))
#print([hex(j) for j in plain])
elif (sign == 'none'):
signature = ''
else:
print("Opening signature file %s" % signature_file)
signfile = open(signature_file, 'rb')
buf = signfile.read(1024)
signfile.close()
if len(buf) != HDR_SIGNATURE_LEN:
print("Wrong signature file size %d, expected %d" % (len(buf), HDR_SIGNATURE_LEN))
sys.exit(4)
signature = buf
if (sign != 'none'):
header += struct.pack('<HH', HDR_SIGNATURE, HDR_SIGNATURE_LEN)
header += signature
#print ("Signature %d" % len(signature))
#print([hex(j) for j in signature])
print ("Done.")
header = make_header(image_file)
# Create output image. Add padded header in front
outfile = open(output_image_file, 'wb')
@ -414,6 +450,29 @@ while True:
infile.close()
outfile.close()
if (delta):
tmp_outfile='/tmp/delta.bin'
os.system('./bmdiff ' + output_image_file + ' ' + delta_base_file + ' ' + tmp_outfile)
base_version = re.split("_", (re.split("_v", delta_base_file)[1]))[0]
header = make_header(tmp_outfile, [[HDR_IMG_DELTA_BASE, 4, struct.pack("<L", int(base_version))]])
outfile = open(delta_output_image_file, 'wb')
outfile.write(header)
sz = len(header)
while sz < WOLFBOOT_HEADER_SIZE:
outfile.write(struct.pack('B', 0xFF))
sz += 1
infile = open(tmp_outfile, 'rb')
while True:
buf = infile.read(1024)
if len(buf) == 0:
break
outfile.write(buf)
infile.close()
outfile.close()
os.remove(tmp_outfile)
if (encrypt):
sz = 0
off = 0

View File

@ -397,6 +397,18 @@ test-171-forward-update-no-downgrade-NOSIGN: $(EXPVER) FORCE
test-173-rollback-NOSIGN: $(EXPVER) FORCE
@make test-03-rollback SIGN=NONE
# Group 18: Delta update
#
#
test-181-forward-update-no-downgrade-delta:SIGN_ARGS+="-delta test-app/image_v1_signed.bin"
test-181-forward-update-no-downgrade-delta: $(EXPVER) FORCE
@make test-01-forward-update-no-downgrade DELTA_UPDATES=1
test-183-rollback-delta:SIGN_ARGS+="-delta test-app/image_v1_signed.bin"
test-183-rollback-delta: $(EXPVER) FORCE
@make test-03-rollback DELTA_UPDATES=1
# Groups 20:31,37: Combinations of previous tests with WOLFBOOT_SMALL_STACK
#
#
@ -689,6 +701,16 @@ test-fastmath-smallstack: clean
make test-1111-fastmath-smallstack-forward-update-no-downgrade-RSA4096-SHA3
make test-1171-fastmath-smallstack-forward-update-no-downgrade-NOSIGN
test-delta-update: clean
@echo Delta update tests
@echo ==========
@echo
@echo
@make test-181-forward-update-no-downgrade-delta
@make test-183-rollback-delta
test-all: clean
make test-base
@ -700,4 +722,4 @@ test-all: clean
make test-no-asm
make test-no-asm-smallstack
make test-fastmath-smallstack
make test-delta-update