|
40 | 40 | from .boot_record import create_sw_component_data |
41 | 41 | from .keys import rsa, ecdsa, x25519 |
42 | 42 |
|
| 43 | +from collections import namedtuple |
| 44 | + |
43 | 45 | IMAGE_MAGIC = 0x96f3b83d |
44 | 46 | IMAGE_HEADER_SIZE = 32 |
45 | 47 | BIN_EXT = "bin" |
|
65 | 67 | 'PUBKEY': 0x02, |
66 | 68 | 'SHA256': 0x10, |
67 | 69 | 'SHA384': 0x11, |
| 70 | + 'SHA512': 0x12, |
68 | 71 | 'RSA2048': 0x20, |
69 | 72 | 'ECDSASIG': 0x22, |
70 | 73 | 'RSA3072': 0x23, |
@@ -135,21 +138,90 @@ def get(self): |
135 | 138 | return header + bytes(self.buf) |
136 | 139 |
|
137 | 140 |
|
| 141 | +SHAAndAlgT = namedtuple('SHAAndAlgT', ['sha', 'alg']) |
| 142 | + |
| 143 | +TLV_SHA_TO_SHA_AND_ALG = { |
| 144 | + TLV_VALUES['SHA256'] : SHAAndAlgT('256', hashlib.sha256), |
| 145 | + TLV_VALUES['SHA384'] : SHAAndAlgT('384', hashlib.sha384), |
| 146 | + TLV_VALUES['SHA512'] : SHAAndAlgT('512', hashlib.sha512), |
| 147 | +} |
| 148 | + |
| 149 | + |
| 150 | +USER_SHA_TO_ALG_AND_TLV = { |
| 151 | + 'auto' : (hashlib.sha256, 'SHA256'), |
| 152 | + '256' : (hashlib.sha256, 'SHA256'), |
| 153 | + '384' : (hashlib.sha384, 'SHA384'), |
| 154 | + '512' : (hashlib.sha512, 'SHA512') |
| 155 | +} |
| 156 | + |
| 157 | + |
| 158 | +def is_sha_tlv(tlv): |
| 159 | + return tlv in TLV_SHA_TO_SHA_AND_ALG.keys() |
| 160 | + |
| 161 | + |
| 162 | +def tlv_sha_to_sha(tlv): |
| 163 | + return TLV_SHA_TO_SHA_AND_ALG[tlv].sha |
| 164 | + |
| 165 | + |
| 166 | +# Auto selecting hash algorithm for type(key) |
| 167 | +ALLOWED_KEY_SHA = { |
| 168 | + keys.ECDSA384P1 : ['384'], |
| 169 | + keys.ECDSA384P1Public : ['384'], |
| 170 | + keys.ECDSA256P1 : ['256'], |
| 171 | + keys.RSA : ['256'], |
| 172 | + # This two are set to 256 for compatibility, the right would be 512 |
| 173 | + keys.Ed25519 : ['256', '512'], |
| 174 | + keys.X25519 : ['256', '512'] |
| 175 | +} |
| 176 | + |
| 177 | +def key_and_user_sha_to_alg_and_tlv(key, user_sha): |
| 178 | + """Matches key and user requested sha to sha alogrithm and TLV name. |
| 179 | +
|
| 180 | + The returned tuple will contain hash functions and TVL name. |
| 181 | + The function is designed to succeed or completely fail execution, |
| 182 | + as providing incorrect pair here basically prevents doing |
| 183 | + any more work. |
| 184 | + """ |
| 185 | + if key is None: |
| 186 | + # If key is none, we allow whatever user has selected for sha |
| 187 | + return USER_SHA_TO_ALG_AND_TLV[user_sha] |
| 188 | + |
| 189 | + # If key is not None, then we have to filter hash to only allowed |
| 190 | + allowed = None |
| 191 | + try: |
| 192 | + allowed = ALLOWED_KEY_SHA[type(key)] |
| 193 | + except KeyError: |
| 194 | + raise click.UsageError("Colud not find allowed hash algorithms for {}" |
| 195 | + .format(type(key))) |
| 196 | + if user_sha == 'auto': |
| 197 | + return USER_SHA_TO_ALG_AND_TLV[allowed[0]] |
| 198 | + |
| 199 | + if user_sha in allowed: |
| 200 | + return USER_SHA_TO_ALG_AND_TLV[user_sha] |
| 201 | + |
| 202 | + raise click.UsageError("Key {} can not be used with --sha {}; allowed sha are one of {}" |
| 203 | + .format(key.sig_type(), user_sha, allowed)) |
| 204 | + |
| 205 | + |
138 | 206 | def get_digest(tlv_type, hash_region): |
139 | | - if tlv_type == TLV_VALUES["SHA384"]: |
140 | | - sha = hashlib.sha384() |
141 | | - elif tlv_type == TLV_VALUES["SHA256"]: |
142 | | - sha = hashlib.sha256() |
| 207 | + sha = TLV_SHA_TO_SHA_AND_ALG[tlv_type].alg() |
143 | 208 |
|
144 | 209 | sha.update(hash_region) |
145 | 210 | return sha.digest() |
146 | 211 |
|
147 | 212 |
|
148 | 213 | def tlv_matches_key_type(tlv_type, key): |
149 | 214 | """Check if provided key matches to TLV record in the image""" |
150 | | - return (key is None or |
151 | | - type(key) == keys.ECDSA384P1 and tlv_type == TLV_VALUES["SHA384"] or |
152 | | - type(key) != keys.ECDSA384P1 and tlv_type == TLV_VALUES["SHA256"]) |
| 215 | + try: |
| 216 | + # We do not need the result here, and the key_and_user_sha_to_alg_and_tlv |
| 217 | + # will either succeed finding match or rise exception, so on success we |
| 218 | + # return True, on exception we return False. |
| 219 | + _, _ = key_and_user_sha_to_alg_and_tlv(key, tlv_sha_to_sha(tlv_type)) |
| 220 | + return True |
| 221 | + except: |
| 222 | + pass |
| 223 | + |
| 224 | + return False |
153 | 225 |
|
154 | 226 |
|
155 | 227 | class Image: |
@@ -336,17 +408,13 @@ def ecies_hkdf(self, enckey, plainkey): |
336 | 408 |
|
337 | 409 | def create(self, key, public_key_format, enckey, dependencies=None, |
338 | 410 | sw_type=None, custom_tlvs=None, encrypt_keylen=128, clear=False, |
339 | | - fixed_sig=None, pub_key=None, vector_to_sign=None): |
| 411 | + fixed_sig=None, pub_key=None, vector_to_sign=None, user_sha='auto'): |
340 | 412 | self.enckey = enckey |
341 | 413 |
|
342 | | - # Check what hashing algorithm should be used |
343 | | - if (key and isinstance(key, ecdsa.ECDSA384P1) |
344 | | - or pub_key and isinstance(pub_key, ecdsa.ECDSA384P1Public)): |
345 | | - hash_algorithm = hashlib.sha384 |
346 | | - hash_tlv = "SHA384" |
347 | | - else: |
348 | | - hash_algorithm = hashlib.sha256 |
349 | | - hash_tlv = "SHA256" |
| 414 | + # key decides on sha, then pub_key; of both are none default is used |
| 415 | + check_key = key if key is not None else pub_key |
| 416 | + hash_algorithm, hash_tlv = key_and_user_sha_to_alg_and_tlv(check_key, user_sha) |
| 417 | + |
350 | 418 | # Calculate the hash of the public key |
351 | 419 | if key is not None: |
352 | 420 | pub = key.get_public_bytes() |
@@ -466,11 +534,14 @@ def create(self, key, public_key_format, enckey, dependencies=None, |
466 | 534 |
|
467 | 535 | tlv = TLV(self.endian) |
468 | 536 |
|
469 | | - # Note that ecdsa wants to do the hashing itself, which means |
470 | | - # we get to hash it twice. |
| 537 | + # These signature is done over sha of image. In case of |
| 538 | + # EC signatures so called Pure algorithm, designated to be run |
| 539 | + # over entire message is used with sha of image as message, |
| 540 | + # so, for example, in case of ED25519 we have here SHAxxx-ED25519-SHA512. |
471 | 541 | sha = hash_algorithm() |
472 | 542 | sha.update(self.payload) |
473 | 543 | digest = sha.digest() |
| 544 | + message = digest; |
474 | 545 | tlv.add(hash_tlv, digest) |
475 | 546 |
|
476 | 547 | if vector_to_sign == 'payload': |
@@ -499,7 +570,7 @@ def create(self, key, public_key_format, enckey, dependencies=None, |
499 | 570 | sig = key.sign(bytes(self.payload)) |
500 | 571 | else: |
501 | 572 | print(os.path.basename(__file__) + ": sign the digest") |
502 | | - sig = key.sign_digest(digest) |
| 573 | + sig = key.sign_digest(message) |
503 | 574 | tlv.add(key.sig_tlv(), sig) |
504 | 575 | self.signature = sig |
505 | 576 | elif fixed_sig is not None and key is None: |
@@ -678,7 +749,7 @@ def verify(imgfile, key): |
678 | 749 | while tlv_off < tlv_end: |
679 | 750 | tlv = b[tlv_off:tlv_off + TLV_SIZE] |
680 | 751 | tlv_type, _, tlv_len = struct.unpack('BBH', tlv) |
681 | | - if tlv_type == TLV_VALUES["SHA256"] or tlv_type == TLV_VALUES["SHA384"]: |
| 752 | + if is_sha_tlv(tlv_type): |
682 | 753 | if not tlv_matches_key_type(tlv_type, key): |
683 | 754 | return VerifyResult.KEY_MISMATCH, None, None |
684 | 755 | off = tlv_off + TLV_SIZE |
|
0 commit comments