Implement new swap scheme for devices with large erase size using scratch with status area
diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py
index 257c892..ec18fa5 100644
--- a/scripts/imgtool/image.py
+++ b/scripts/imgtool/image.py
@@ -157,6 +157,9 @@
self.enckey = None
self.save_enctlv = save_enctlv
self.enctlv_len = 0
+ self.hkdf_salt = None
+ self.hkdf_len = 48
+ self.enc_nonce = bytes([0] * 16)
if security_counter == 'auto':
# Security counter has not been explicitly provided,
@@ -228,7 +231,7 @@
self.save_enctlv,
self.enctlv_len)
trailer_addr = (self.base_addr + self.slot_size) - trailer_size
- padding = bytearray([self.erased_val] *
+ padding = bytearray([self.erased_val] *
(trailer_size - len(boot_magic)))
if self.confirm and not self.overwrite_only:
padding[-MAX_ALIGN] = 0x01 # image_ok = 0x01
@@ -267,13 +270,18 @@
newpk = X25519PrivateKey.generate()
shared = newpk.exchange(enckey._get_public())
derived_key = HKDF(
- algorithm=hashes.SHA256(), length=48, salt=None,
+ algorithm=hashes.SHA256(), length=self.hkdf_len, salt=self.hkdf_salt,
info=b'MCUBoot_ECIES_v1', backend=default_backend()).derive(shared)
+ if self.hkdf_salt is not None:
+ key_nonce = derived_key[48:64]
+ self.enc_nonce = derived_key[64:76] + bytes([0] * 4)
+ else:
+ key_nonce = bytes([0] * 16)
encryptor = Cipher(algorithms.AES(derived_key[:16]),
- modes.CTR(bytes([0] * 16)),
+ modes.CTR(key_nonce),
backend=default_backend()).encryptor()
cipherkey = encryptor.update(plainkey) + encryptor.finalize()
- mac = hmac.HMAC(derived_key[16:], hashes.SHA256(),
+ mac = hmac.HMAC(derived_key[16:48], hashes.SHA256(),
backend=default_backend())
mac.update(cipherkey)
ciphermac = mac.finalize()
@@ -288,9 +296,13 @@
return cipherkey, ciphermac, pubk
def create(self, key, public_key_format, enckey, dependencies=None,
- sw_type=None, custom_tlvs=None):
+ sw_type=None, custom_tlvs=None, use_random_iv=False):
self.enckey = enckey
+ if use_random_iv:
+ self.hkdf_salt = os.urandom(32)
+ self.hkdf_len += 16 * 2 # 48 for basic scheme + 16 * 2 for random IVs
+
# Calculate the hash of the public key
if key is not None:
pub = key.get_public_bytes()
@@ -443,13 +455,15 @@
x25519.X25519Public)):
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey)
enctlv = pubk + mac + cipherkey
+ if self.hkdf_salt is not None:
+ enctlv += self.hkdf_salt
self.enctlv_len = len(enctlv)
if isinstance(enckey, ecdsa.ECDSA256P1Public):
tlv.add('ENCEC256', enctlv)
else:
tlv.add('ENCX25519', enctlv)
- nonce = bytes([0] * 16)
+ nonce = self.enc_nonce
cipher = Cipher(algorithms.AES(plainkey), modes.CTR(nonce),
backend=default_backend())
encryptor = cipher.encryptor()
diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py
index 6188e78..53f4d98 100755
--- a/scripts/imgtool/main.py
+++ b/scripts/imgtool/main.py
@@ -290,6 +290,9 @@
default='hash', help='In what format to add the public key to '
'the image manifest: full key or hash of the key.')
@click.option('-k', '--key', metavar='filename')
+@click.option('--use-random-iv', default=False, is_flag=True,
+ help='Use random Salt and IV (initial vectors) for the image '
+ 'encrypting scheme')
@click.command(help='''Create a signed or unsigned image\n
INFILE and OUTFILE are parsed as Intel HEX if the params have
.hex extension, otherwise binary format is used''')
@@ -297,7 +300,7 @@
pad_header, slot_size, pad, confirm, max_sectors, overwrite_only,
endian, encrypt, infile, outfile, dependencies, load_addr, hex_addr,
erased_val, save_enctlv, security_counter, boot_record, custom_tlv,
- rom_fixed):
+ rom_fixed, use_random_iv):
if confirm:
# Confirmed but non-padded images don't make much sense, because
@@ -344,7 +347,7 @@
custom_tlvs[tag] = value.encode('utf-8')
img.create(key, public_key_format, enckey, dependencies, boot_record,
- custom_tlvs)
+ custom_tlvs, use_random_iv=use_random_iv)
img.save(outfile, hex_addr)