Skip to content

Commit 7b05123

Browse files
committed
imgtool: Add support for HMAC/HKDF-SHA512 with ECIES-X25519
Commit adds imgtool command line option --hmac-sha allowing to select between SHA256 and SHA512 for HMAC/HKDF. Signed-off-by: Dominik Ermel <[email protected]>
1 parent 6df4ecf commit 7b05123

File tree

2 files changed

+34
-14
lines changed

2 files changed

+34
-14
lines changed

scripts/imgtool/image.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
'ENCKW': 0x31,
8989
'ENCEC256': 0x32,
9090
'ENCX25519': 0x33,
91+
'ENCX25519_SHA512': 0x34,
9192
'DEPENDENCY': 0x40,
9293
'SEC_CNT': 0x50,
9394
'BOOT_RECORD': 0x60,
@@ -429,21 +430,21 @@ def check_trailer(self):
429430
len(self.payload), tsize, self.slot_size)
430431
raise click.UsageError(msg)
431432

432-
def ecies_hkdf(self, enckey, plainkey):
433+
def ecies_hkdf(self, enckey, plainkey, hmac_sha_alg):
433434
if isinstance(enckey, ecdsa.ECDSA256P1Public):
434435
newpk = ec.generate_private_key(ec.SECP256R1(), default_backend())
435436
shared = newpk.exchange(ec.ECDH(), enckey._get_public())
436437
else:
437438
newpk = X25519PrivateKey.generate()
438439
shared = newpk.exchange(enckey._get_public())
439440
derived_key = HKDF(
440-
algorithm=hashes.SHA256(), length=48, salt=None,
441+
algorithm=hmac_sha_alg, length=48, salt=None,
441442
info=b'MCUBoot_ECIES_v1', backend=default_backend()).derive(shared)
442443
encryptor = Cipher(algorithms.AES(derived_key[:16]),
443444
modes.CTR(bytes([0] * 16)),
444445
backend=default_backend()).encryptor()
445446
cipherkey = encryptor.update(plainkey) + encryptor.finalize()
446-
mac = hmac.HMAC(derived_key[16:], hashes.SHA256(),
447+
mac = hmac.HMAC(derived_key[16:], hmac_sha_alg,
447448
backend=default_backend())
448449
mac.update(cipherkey)
449450
ciphermac = mac.finalize()
@@ -461,7 +462,8 @@ def create(self, key, public_key_format, enckey, dependencies=None,
461462
sw_type=None, custom_tlvs=None, compression_tlvs=None,
462463
compression_type=None, encrypt_keylen=128, clear=False,
463464
fixed_sig=None, pub_key=None, vector_to_sign=None,
464-
user_sha='auto', is_pure=False, keep_comp_size=False, dont_encrypt=False):
465+
user_sha='auto', hmac_sha='auto', is_pure=False, keep_comp_size=False,
466+
dont_encrypt=False):
465467
self.enckey = enckey
466468

467469
# key decides on sha, then pub_key; of both are none default is used
@@ -668,6 +670,17 @@ def create(self, key, public_key_format, enckey, dependencies=None,
668670
else:
669671
plainkey = os.urandom(16)
670672

673+
if not isinstance(enckey, rsa.RSAPublic):
674+
if hmac_sha == 'auto' or hmac_sha == '256':
675+
hmac_sha = '256'
676+
hmac_sha_alg = hashes.SHA256()
677+
elif hmac_sha == '512':
678+
if not isinstance(enckey, x25519.X25519Public):
679+
raise click.UsageError("Currently only ECIES-X2519 supports HMAC-SHA512")
680+
hmac_sha_alg = hashes.SHA512()
681+
else:
682+
raise click.UsageError("Unsupported HMAC-SHA")
683+
671684
if isinstance(enckey, rsa.RSAPublic):
672685
cipherkey = enckey._get_public().encrypt(
673686
plainkey, padding.OAEP(
@@ -676,15 +689,19 @@ def create(self, key, public_key_format, enckey, dependencies=None,
676689
label=None))
677690
self.enctlv_len = len(cipherkey)
678691
tlv.add('ENCRSA2048', cipherkey)
679-
elif isinstance(enckey, (ecdsa.ECDSA256P1Public,
680-
x25519.X25519Public)):
681-
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey)
692+
elif isinstance(enckey, ecdsa.ECDSA256P1Public):
693+
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg)
682694
enctlv = pubk + mac + cipherkey
683695
self.enctlv_len = len(enctlv)
684-
if isinstance(enckey, ecdsa.ECDSA256P1Public):
685-
tlv.add('ENCEC256', enctlv)
686-
else:
696+
tlv.add('ENCEC256', enctlv)
697+
elif isinstance(enckey, x25519.X25519Public):
698+
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg)
699+
enctlv = pubk + mac + cipherkey
700+
self.enctlv_len = len(enctlv)
701+
if (hmac_sha == '256'):
687702
tlv.add('ENCX25519', enctlv)
703+
else:
704+
tlv.add('ENCX25519_SHA512', enctlv)
688705

689706
if not clear:
690707
nonce = bytes([0] * 16)

scripts/imgtool/main.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def gen_x25519(keyfile, passwd):
8484
}
8585
valid_formats = ['openssl', 'pkcs8']
8686
valid_sha = [ 'auto', '256', '384', '512' ]
87+
valid_hmac_sha = [ 'auto', '256', '512' ]
8788

8889

8990
def load_signature(sigfile):
@@ -437,6 +438,8 @@ def convert(self, value, param, ctx):
437438
@click.option('--sha', 'user_sha', type=click.Choice(valid_sha), default='auto',
438439
help='selected sha algorithm to use; defaults to "auto" which is 256 if '
439440
'no cryptographic signature is used, or default for signature type')
441+
@click.option('--hmac-sha', 'hmac_sha', type=click.Choice(valid_hmac_sha), default='auto',
442+
help='sha algorithm used in HKDF/HMAC in ECIES key exchange TLV')
440443
@click.option('--vector-to-sign', type=click.Choice(['payload', 'digest']),
441444
help='send to OUTFILE the payload or payload''s digest instead '
442445
'of complied image. These data can be used for external image '
@@ -449,7 +452,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
449452
endian, encrypt_keylen, encrypt, compression, infile, outfile,
450453
dependencies, load_addr, hex_addr, erased_val, save_enctlv,
451454
security_counter, boot_record, custom_tlv, rom_fixed, max_align,
452-
clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, is_pure,
455+
clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, hmac_sha, is_pure,
453456
vector_to_sign, non_bootable):
454457

455458
if confirm:
@@ -526,7 +529,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
526529
img.create(key, public_key_format, enckey, dependencies, boot_record,
527530
custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear,
528531
baked_signature, pub_key, vector_to_sign, user_sha=user_sha,
529-
is_pure=is_pure, keep_comp_size=False, dont_encrypt=True)
532+
hmac_sha=hmac_sha, is_pure=is_pure, keep_comp_size=False, dont_encrypt=True)
530533
compressed_img = image.Image(version=decode_version(version),
531534
header_size=header_size, pad_header=pad_header,
532535
pad=pad, confirm=confirm, align=int(align),
@@ -568,14 +571,14 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
568571
compressed_img.create(key, public_key_format, enckey,
569572
dependencies, boot_record, custom_tlvs, compression_tlvs,
570573
compression, int(encrypt_keylen), clear, baked_signature,
571-
pub_key, vector_to_sign, user_sha=user_sha,
574+
pub_key, vector_to_sign, user_sha=user_sha, hmac_sha=hmac_sha,
572575
is_pure=is_pure, keep_comp_size=keep_comp_size)
573576
img = compressed_img
574577
else:
575578
img.create(key, public_key_format, enckey, dependencies, boot_record,
576579
custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear,
577580
baked_signature, pub_key, vector_to_sign, user_sha=user_sha,
578-
is_pure=is_pure)
581+
hmac_sha=hmac_sha, is_pure=is_pure)
579582
img.save(outfile, hex_addr)
580583
if sig_out is not None:
581584
new_signature = img.get_signature()

0 commit comments

Comments
 (0)