Skip to content

Commit 518f74a

Browse files
de-nordicnvlsianpu
authored andcommitted
[nrf fromtree] 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]> (cherry picked from commit a36f951)
1 parent 210b738 commit 518f74a

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,
@@ -435,21 +436,21 @@ def check_trailer(self):
435436
len(self.payload), tsize, self.slot_size)
436437
raise click.UsageError(msg)
437438

438-
def ecies_hkdf(self, enckey, plainkey):
439+
def ecies_hkdf(self, enckey, plainkey, hmac_sha_alg):
439440
if isinstance(enckey, ecdsa.ECDSA256P1Public):
440441
newpk = ec.generate_private_key(ec.SECP256R1(), default_backend())
441442
shared = newpk.exchange(ec.ECDH(), enckey._get_public())
442443
else:
443444
newpk = X25519PrivateKey.generate()
444445
shared = newpk.exchange(enckey._get_public())
445446
derived_key = HKDF(
446-
algorithm=hashes.SHA256(), length=48, salt=None,
447+
algorithm=hmac_sha_alg, length=48, salt=None,
447448
info=b'MCUBoot_ECIES_v1', backend=default_backend()).derive(shared)
448449
encryptor = Cipher(algorithms.AES(derived_key[:16]),
449450
modes.CTR(bytes([0] * 16)),
450451
backend=default_backend()).encryptor()
451452
cipherkey = encryptor.update(plainkey) + encryptor.finalize()
452-
mac = hmac.HMAC(derived_key[16:], hashes.SHA256(),
453+
mac = hmac.HMAC(derived_key[16:], hmac_sha_alg,
453454
backend=default_backend())
454455
mac.update(cipherkey)
455456
ciphermac = mac.finalize()
@@ -467,7 +468,8 @@ def create(self, key, public_key_format, enckey, dependencies=None,
467468
sw_type=None, custom_tlvs=None, compression_tlvs=None,
468469
compression_type=None, encrypt_keylen=128, clear=False,
469470
fixed_sig=None, pub_key=None, vector_to_sign=None,
470-
user_sha='auto', is_pure=False, keep_comp_size=False, dont_encrypt=False):
471+
user_sha='auto', hmac_sha='auto', is_pure=False, keep_comp_size=False,
472+
dont_encrypt=False):
471473
self.enckey = enckey
472474

473475
# key decides on sha, then pub_key; of both are none default is used
@@ -674,6 +676,17 @@ def create(self, key, public_key_format, enckey, dependencies=None,
674676
else:
675677
plainkey = os.urandom(16)
676678

679+
if not isinstance(enckey, rsa.RSAPublic):
680+
if hmac_sha == 'auto' or hmac_sha == '256':
681+
hmac_sha = '256'
682+
hmac_sha_alg = hashes.SHA256()
683+
elif hmac_sha == '512':
684+
if not isinstance(enckey, x25519.X25519Public):
685+
raise click.UsageError("Currently only ECIES-X25519 supports HMAC-SHA512")
686+
hmac_sha_alg = hashes.SHA512()
687+
else:
688+
raise click.UsageError("Unsupported HMAC-SHA")
689+
677690
if isinstance(enckey, rsa.RSAPublic):
678691
cipherkey = enckey._get_public().encrypt(
679692
plainkey, padding.OAEP(
@@ -682,15 +695,19 @@ def create(self, key, public_key_format, enckey, dependencies=None,
682695
label=None))
683696
self.enctlv_len = len(cipherkey)
684697
tlv.add('ENCRSA2048', cipherkey)
685-
elif isinstance(enckey, (ecdsa.ECDSA256P1Public,
686-
x25519.X25519Public)):
687-
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey)
698+
elif isinstance(enckey, ecdsa.ECDSA256P1Public):
699+
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg)
688700
enctlv = pubk + mac + cipherkey
689701
self.enctlv_len = len(enctlv)
690-
if isinstance(enckey, ecdsa.ECDSA256P1Public):
691-
tlv.add('ENCEC256', enctlv)
692-
else:
702+
tlv.add('ENCEC256', enctlv)
703+
elif isinstance(enckey, x25519.X25519Public):
704+
cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg)
705+
enctlv = pubk + mac + cipherkey
706+
self.enctlv_len = len(enctlv)
707+
if (hmac_sha == '256'):
693708
tlv.add('ENCX25519', enctlv)
709+
else:
710+
tlv.add('ENCX25519_SHA512', enctlv)
694711

695712
if not clear:
696713
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),
@@ -570,14 +573,14 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
570573
compressed_img.create(key, public_key_format, enckey,
571574
dependencies, boot_record, custom_tlvs, compression_tlvs,
572575
compression, int(encrypt_keylen), clear, baked_signature,
573-
pub_key, vector_to_sign, user_sha=user_sha,
576+
pub_key, vector_to_sign, user_sha=user_sha, hmac_sha=hmac_sha,
574577
is_pure=is_pure, keep_comp_size=keep_comp_size)
575578
img = compressed_img
576579
else:
577580
img.create(key, public_key_format, enckey, dependencies, boot_record,
578581
custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear,
579582
baked_signature, pub_key, vector_to_sign, user_sha=user_sha,
580-
is_pure=is_pure)
583+
hmac_sha=hmac_sha, is_pure=is_pure)
581584
img.save(outfile, hex_addr)
582585
if sig_out is not None:
583586
new_signature = img.get_signature()

0 commit comments

Comments
 (0)