@@ -297,6 +297,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE,
297
297
self .enctlv_len = 0
298
298
self .max_align = max (DEFAULT_MAX_ALIGN , align ) if max_align is None else int (max_align )
299
299
self .non_bootable = non_bootable
300
+ self .key_ids = None
300
301
301
302
if self .max_align == DEFAULT_MAX_ALIGN :
302
303
self .boot_magic = bytes ([
@@ -464,32 +465,40 @@ def ecies_hkdf(self, enckey, plainkey):
464
465
format = PublicFormat .Raw )
465
466
return cipherkey , ciphermac , pubk
466
467
467
- def create (self , key , public_key_format , enckey , dependencies = None ,
468
+ def create (self , keys , public_key_format , enckey , dependencies = None ,
468
469
sw_type = None , custom_tlvs = None , compression_tlvs = None ,
469
470
compression_type = None , encrypt_keylen = 128 , clear = False ,
470
471
fixed_sig = None , pub_key = None , vector_to_sign = None ,
471
472
user_sha = 'auto' , is_pure = False , keep_comp_size = False , dont_encrypt = False ):
472
473
self .enckey = enckey
473
474
474
- # key decides on sha, then pub_key; of both are none default is used
475
- check_key = key if key is not None else pub_key
475
+ # key decides on sha, then pub_key; if both are none default is used
476
+ check_key = keys [ 0 ] if keys [ 0 ] is not None else pub_key
476
477
hash_algorithm , hash_tlv = key_and_user_sha_to_alg_and_tlv (check_key , user_sha , is_pure )
477
478
478
479
# Calculate the hash of the public key
479
- if key is not None :
480
- pub = key .get_public_bytes ()
481
- sha = hash_algorithm ()
482
- sha .update (pub )
483
- pubbytes = sha .digest ()
484
- elif pub_key is not None :
485
- if hasattr (pub_key , 'sign' ):
486
- print (os .path .basename (__file__ ) + ": sign the payload" )
487
- pub = pub_key .get_public_bytes ()
488
- sha = hash_algorithm ()
489
- sha .update (pub )
490
- pubbytes = sha .digest ()
480
+ pub_digests = []
481
+ pub_list = []
482
+
483
+ if keys is None :
484
+ if pub_key is not None :
485
+ if hasattr (pub_key , 'sign' ):
486
+ print (os .path .basename (__file__ ) + ": sign the payload" )
487
+ pub = pub_key .get_public_bytes ()
488
+ sha = hash_algorithm ()
489
+ sha .update (pub )
490
+ pubbytes = sha .digest ()
491
+ else :
492
+ pubbytes = bytes (hashlib .sha256 ().digest_size )
491
493
else :
492
- pubbytes = bytes (hashlib .sha256 ().digest_size )
494
+ for key in keys or []:
495
+ pub = key .get_public_bytes ()
496
+ sha = hash_algorithm ()
497
+ sha .update (pub )
498
+ pubbytes = sha .digest ()
499
+ pub_digests .append (pubbytes )
500
+ pub_list .append (pub )
501
+
493
502
494
503
protected_tlv_size = 0
495
504
@@ -517,10 +526,14 @@ def create(self, key, public_key_format, enckey, dependencies=None,
517
526
# value later.
518
527
digest = bytes (hash_algorithm ().digest_size )
519
528
529
+ if pub_digests :
530
+ boot_pub_digest = pub_digests [0 ]
531
+ else :
532
+ boot_pub_digest = pubbytes
520
533
# Create CBOR encoded boot record
521
534
boot_record = create_sw_component_data (sw_type , image_version ,
522
535
hash_tlv , digest ,
523
- pubbytes )
536
+ boot_pub_digest )
524
537
525
538
protected_tlv_size += TLV_SIZE + len (boot_record )
526
539
@@ -639,33 +652,39 @@ def create(self, key, public_key_format, enckey, dependencies=None,
639
652
print (os .path .basename (__file__ ) + ': export digest' )
640
653
return
641
654
642
- if self . key_ids is not None :
643
- self . _add_key_id_tlv_to_unprotected ( tlv , self . key_ids [ 0 ] )
655
+ if fixed_sig is not None and keys is not None :
656
+ raise click . UsageError ( "Can not sign using key and provide fixed-signature at the same time" )
644
657
645
- if key is not None or fixed_sig is not None :
646
- if public_key_format == 'hash' :
647
- tlv .add ('KEYHASH' , pubbytes )
648
- else :
649
- tlv .add ('PUBKEY' , pub )
658
+ if fixed_sig is not None :
659
+ tlv .add (pub_key .sig_tlv (), fixed_sig ['value' ])
660
+ self .signatures [0 ] = fixed_sig ['value' ]
661
+ else :
662
+ # Multi-signature handling: iterate through each provided key and sign.
663
+ self .signatures = []
664
+ for i , key in enumerate (keys ):
665
+ # If key IDs are provided, and we have enough for this key, add it first.
666
+ if self .key_ids is not None and len (self .key_ids ) > i :
667
+ # Convert key id (an integer) to 4-byte big-endian bytes.
668
+ kid_bytes = self .key_ids [i ].to_bytes (4 , 'big' )
669
+ tlv .add ('KEYID' , kid_bytes ) # Using the TLV tag that corresponds to key IDs.
670
+
671
+ if public_key_format == 'hash' :
672
+ tlv .add ('KEYHASH' , pub_digests [i ])
673
+ else :
674
+ tlv .add ('PUBKEY' , pub_list [i ])
650
675
651
- if key is not None and fixed_sig is None :
652
676
# `sign` expects the full image payload (hashing done
653
677
# internally), while `sign_digest` expects only the digest
654
678
# of the payload
655
-
656
679
if hasattr (key , 'sign' ):
657
680
print (os .path .basename (__file__ ) + ": sign the payload" )
658
681
sig = key .sign (bytes (self .payload ))
659
682
else :
660
683
print (os .path .basename (__file__ ) + ": sign the digest" )
661
684
sig = key .sign_digest (message )
662
685
tlv .add (key .sig_tlv (), sig )
663
- self .signature = sig
664
- elif fixed_sig is not None and key is None :
665
- tlv .add (pub_key .sig_tlv (), fixed_sig ['value' ])
666
- self .signature = fixed_sig ['value' ]
667
- else :
668
- raise click .UsageError ("Can not sign using key and provide fixed-signature at the same time" )
686
+ self .signatures .append (sig )
687
+
669
688
670
689
# At this point the image was hashed + signed, we can remove the
671
690
# protected TLVs from the payload (will be re-added later)
@@ -714,7 +733,7 @@ def get_struct_endian(self):
714
733
return STRUCT_ENDIAN_DICT [self .endian ]
715
734
716
735
def get_signature (self ):
717
- return self .signature
736
+ return self .signatures
718
737
719
738
def get_infile_data (self ):
720
739
return self .infile_data
@@ -824,75 +843,99 @@ def verify(imgfile, key):
824
843
if magic != IMAGE_MAGIC :
825
844
return VerifyResult .INVALID_MAGIC , None , None , None
826
845
846
+ # Locate the first TLV info header
827
847
tlv_off = header_size + img_size
828
848
tlv_info = b [tlv_off :tlv_off + TLV_INFO_SIZE ]
829
849
magic , tlv_tot = struct .unpack ('HH' , tlv_info )
850
+
851
+ # If it's the protected-TLV block, skip it
830
852
if magic == TLV_PROT_INFO_MAGIC :
831
- tlv_off += tlv_tot
853
+ tlv_off += TLV_INFO_SIZE + tlv_tot
832
854
tlv_info = b [tlv_off :tlv_off + TLV_INFO_SIZE ]
833
855
magic , tlv_tot = struct .unpack ('HH' , tlv_info )
834
856
835
857
if magic != TLV_INFO_MAGIC :
836
858
return VerifyResult .INVALID_TLV_INFO_MAGIC , None , None , None
837
859
838
- # This is set by existence of TLV SIG_PURE
839
- is_pure = False
860
+ # Define the unprotected-TLV window
861
+ unprot_off = tlv_off + TLV_INFO_SIZE
862
+ unprot_end = unprot_off + tlv_tot
840
863
841
- prot_tlv_size = tlv_off
842
- hash_region = b [:prot_tlv_size ]
843
- tlv_end = tlv_off + tlv_tot
844
- tlv_off += TLV_INFO_SIZE # skip tlv info
864
+ # Region up to the start of unprotected TLVs is hashed
865
+ prot_tlv_end = unprot_off - TLV_INFO_SIZE
866
+ hash_region = b [:prot_tlv_end ]
845
867
846
- # First scan all TLVs in search of SIG_PURE
847
- while tlv_off < tlv_end :
848
- tlv = b [tlv_off :tlv_off + TLV_SIZE ]
868
+ # This is set by existence of TLV SIG_PURE
869
+ is_pure = False
870
+ scan_off = unprot_off
871
+ while scan_off < unprot_end :
872
+ tlv = b [scan_off :scan_off + TLV_SIZE ]
849
873
tlv_type , _ , tlv_len = struct .unpack ('BBH' , tlv )
850
874
if tlv_type == TLV_VALUES ['SIG_PURE' ]:
851
875
is_pure = True
852
876
break
853
- tlv_off += TLV_SIZE + tlv_len
877
+ scan_off += TLV_SIZE + tlv_len
854
878
879
+ if key is not None and not isinstance (key , list ):
880
+ key = [key ]
881
+
882
+ verify_results = []
883
+ scan_off = unprot_off
855
884
digest = None
856
- tlv_off = prot_tlv_size
857
- tlv_end = tlv_off + tlv_tot
858
- tlv_off += TLV_INFO_SIZE # skip tlv info
859
- while tlv_off < tlv_end :
860
- tlv = b [tlv_off : tlv_off + TLV_SIZE ]
885
+ prot_tlv_size = unprot_off - TLV_INFO_SIZE
886
+
887
+ # Verify hash and signatures
888
+ while scan_off < unprot_end :
889
+ tlv = b [scan_off : scan_off + TLV_SIZE ]
861
890
tlv_type , _ , tlv_len = struct .unpack ('BBH' , tlv )
862
891
if is_sha_tlv (tlv_type ):
863
- if not tlv_matches_key_type (tlv_type , key ):
892
+ if not tlv_matches_key_type (tlv_type , key [ 0 ] ):
864
893
return VerifyResult .KEY_MISMATCH , None , None , None
865
- off = tlv_off + TLV_SIZE
894
+ off = scan_off + TLV_SIZE
866
895
digest = get_digest (tlv_type , hash_region )
867
- if digest == b [off :off + tlv_len ]:
868
- if key is None :
869
- return VerifyResult .OK , version , digest , None
870
- else :
871
- return VerifyResult .INVALID_HASH , None , None , None
872
- elif not is_pure and key is not None and tlv_type == TLV_VALUES [key .sig_tlv ()]:
873
- off = tlv_off + TLV_SIZE
874
- tlv_sig = b [off :off + tlv_len ]
875
- payload = b [:prot_tlv_size ]
876
- try :
877
- if hasattr (key , 'verify' ):
878
- key .verify (tlv_sig , payload )
879
- else :
880
- key .verify_digest (tlv_sig , digest )
881
- return VerifyResult .OK , version , digest , None
882
- except InvalidSignature :
883
- # continue to next TLV
884
- pass
896
+ if digest != b [off :off + tlv_len ]:
897
+ verify_results .append (("Digest" , "INVALID_HASH" ))
898
+
899
+ elif not is_pure and key is not None and tlv_type == TLV_VALUES [key [0 ].sig_tlv ()]:
900
+ for idx , k in enumerate (key ):
901
+ if tlv_type == TLV_VALUES [k .sig_tlv ()]:
902
+ off = scan_off + TLV_SIZE
903
+ tlv_sig = b [off :off + tlv_len ]
904
+ payload = b [:prot_tlv_size ]
905
+ try :
906
+ if hasattr (k , 'verify' ):
907
+ k .verify (tlv_sig , payload )
908
+ else :
909
+ k .verify_digest (tlv_sig , digest )
910
+ verify_results .append ((f"Key { idx } " , "OK" ))
911
+ break
912
+ except InvalidSignature :
913
+ # continue to next TLV
914
+ verify_results .append ((f"Key { idx } " , "INVALID_SIGNATURE" ))
915
+ continue
916
+
885
917
elif is_pure and key is not None and tlv_type in ALLOWED_PURE_SIG_TLVS :
886
- off = tlv_off + TLV_SIZE
918
+ # pure signature verification
919
+ off = scan_off + TLV_SIZE
887
920
tlv_sig = b [off :off + tlv_len ]
921
+ k = key [0 ]
888
922
try :
889
- key .verify_digest (tlv_sig , hash_region )
923
+ k .verify_digest (tlv_sig , hash_region )
890
924
return VerifyResult .OK , version , None , tlv_sig
891
925
except InvalidSignature :
892
- # continue to next TLV
893
- pass
894
- tlv_off += TLV_SIZE + tlv_len
895
- return VerifyResult .INVALID_SIGNATURE , None , None , None
926
+ return VerifyResult .INVALID_SIGNATURE , None , None , None
927
+
928
+ scan_off += TLV_SIZE + tlv_len
929
+ # Now print out the verification results:
930
+ for k , result in verify_results :
931
+ print (f"{ k } : { result } " )
932
+
933
+ # Decide on a final return (for example, OK only if at least one signature is valid)
934
+ if any (result == "OK" for _ , result in verify_results ):
935
+ return VerifyResult .OK , version , digest , None
936
+ else :
937
+ return VerifyResult .INVALID_SIGNATURE , None , None , None
938
+
896
939
897
940
def set_key_ids (self , key_ids ):
898
941
"""Set list of key IDs (integers) to be inserted before each signature."""
0 commit comments