22
22
import getpass
23
23
import imgtool .keys as keys
24
24
import sys
25
+ import struct
26
+ import os
27
+ import lzma
28
+ import hashlib
25
29
import base64
26
30
from imgtool import image , imgtool_version
27
31
from imgtool .version import decode_version
28
32
from imgtool .dumpinfo import dump_imginfo
29
33
from .keys import (
30
34
RSAUsageError , ECDSAUsageError , Ed25519UsageError , X25519UsageError )
31
35
36
+ comp_default_dictsize = 131072
37
+ comp_default_pb = 2
38
+ comp_default_lc = 3
39
+ comp_default_lp = 1
40
+ comp_default_preset = 9
41
+
42
+
32
43
MIN_PYTHON_VERSION = (3 , 6 )
33
44
if sys .version_info < MIN_PYTHON_VERSION :
34
45
sys .exit ("Python %s.%s or newer is required by imgtool."
@@ -300,6 +311,14 @@ def get_dependencies(ctx, param, value):
300
311
dependencies [image .DEP_VERSIONS_KEY ] = versions
301
312
return dependencies
302
313
314
+ def create_lzma2_header (dictsize , pb , lc , lp ):
315
+ header = bytearray ()
316
+ for i in range (0 , 40 ):
317
+ if dictsize <= ((2 | ((i ) & 1 )) << int ((i ) / 2 + 11 )):
318
+ header .append (i )
319
+ break
320
+ header .append ( ( pb * 5 + lp ) * 9 + lc )
321
+ return header
303
322
304
323
class BasedIntParamType (click .ParamType ):
305
324
name = 'integer'
@@ -343,6 +362,11 @@ def convert(self, value, param, ctx):
343
362
type = click .Choice (['128' , '256' ]),
344
363
help = 'When encrypting the image using AES, select a 128 bit or '
345
364
'256 bit key len.' )
365
+ @click .option ('--compression' , default = 'disabled' ,
366
+ type = click .Choice (['disabled' , 'lzma2' ]),
367
+ help = 'Enable image compression using specified type. '
368
+ 'Will fall back without image compression automatically '
369
+ 'if the compression increases the image size.' )
346
370
@click .option ('-c' , '--clear' , required = False , is_flag = True , default = False ,
347
371
help = 'Output a non-encrypted image with encryption capabilities,'
348
372
'so it can be installed in the primary slot, and encrypted '
@@ -414,10 +438,11 @@ def convert(self, value, param, ctx):
414
438
.hex extension, otherwise binary format is used''' )
415
439
def sign (key , public_key_format , align , version , pad_sig , header_size ,
416
440
pad_header , slot_size , pad , confirm , max_sectors , overwrite_only ,
417
- endian , encrypt_keylen , encrypt , infile , outfile , dependencies ,
418
- load_addr , hex_addr , erased_val , save_enctlv , security_counter ,
419
- boot_record , custom_tlv , rom_fixed , max_align , clear , fix_sig ,
420
- fix_sig_pubkey , sig_out , user_sha , vector_to_sign , non_bootable ):
441
+ endian , encrypt_keylen , encrypt , compression , infile , outfile ,
442
+ dependencies , load_addr , hex_addr , erased_val , save_enctlv ,
443
+ security_counter , boot_record , custom_tlv , rom_fixed , max_align ,
444
+ clear , fix_sig , fix_sig_pubkey , sig_out , user_sha , vector_to_sign ,
445
+ non_bootable ):
421
446
422
447
if confirm :
423
448
# Confirmed but non-padded images don't make much sense, because
@@ -431,6 +456,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
431
456
erased_val = erased_val , save_enctlv = save_enctlv ,
432
457
security_counter = security_counter , max_align = max_align ,
433
458
non_bootable = non_bootable )
459
+ compression_tlvs = {}
434
460
img .load (infile )
435
461
key = load_key (key ) if key else None
436
462
enckey = load_key (encrypt ) if encrypt else None
@@ -484,10 +510,49 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
484
510
}
485
511
486
512
img .create (key , public_key_format , enckey , dependencies , boot_record ,
487
- custom_tlvs , int (encrypt_keylen ), clear , baked_signature ,
488
- pub_key , vector_to_sign , user_sha )
513
+ custom_tlvs , compression_tlvs , int (encrypt_keylen ), clear ,
514
+ baked_signature , pub_key , vector_to_sign , user_sha )
515
+
516
+ if compression == "lzma2" :
517
+ compressed_img = image .Image (version = decode_version (version ),
518
+ header_size = header_size , pad_header = pad_header ,
519
+ pad = pad , confirm = confirm , align = int (align ),
520
+ slot_size = slot_size , max_sectors = max_sectors ,
521
+ overwrite_only = overwrite_only , endian = endian ,
522
+ load_addr = load_addr , rom_fixed = rom_fixed ,
523
+ erased_val = erased_val , save_enctlv = save_enctlv ,
524
+ security_counter = security_counter , max_align = max_align )
525
+ compression_filters = [
526
+ {"id" : lzma .FILTER_LZMA2 , "preset" : comp_default_preset ,
527
+ "dict_size" : comp_default_dictsize , "lp" : comp_default_lp ,
528
+ "lc" : comp_default_lc }
529
+ ]
530
+ compressed_data = lzma .compress (img .get_infile_data (),filters = compression_filters ,
531
+ format = lzma .FORMAT_RAW )
532
+ uncompressed_size = len (img .get_infile_data ())
533
+ compressed_size = len (compressed_data )
534
+ print (f"compressed image size: { compressed_size } bytes" )
535
+ print (f"original image size: { uncompressed_size } bytes" )
536
+ compression_tlvs ["DECOMP_SIZE" ] = struct .pack (
537
+ img .get_struct_endian () + 'L' , img .image_size )
538
+ compression_tlvs ["DECOMP_SHA" ] = img .image_hash
539
+ compression_tlvs_size = len (compression_tlvs ["DECOMP_SIZE" ])
540
+ compression_tlvs_size += len (compression_tlvs ["DECOMP_SHA" ])
541
+ if img .get_signature ():
542
+ compression_tlvs ["DECOMP_SIGNATURE" ] = img .get_signature ()
543
+ compression_tlvs_size += len (compression_tlvs ["DECOMP_SIGNATURE" ])
544
+ if (compressed_size + compression_tlvs_size ) < uncompressed_size :
545
+ compression_header = create_lzma2_header (
546
+ dictsize = comp_default_dictsize , pb = comp_default_pb ,
547
+ lc = comp_default_lc , lp = comp_default_lp )
548
+ compressed_img .load_compressed (compressed_data , compression_header )
549
+ compressed_img .base_addr = img .base_addr
550
+ compressed_img .create (key , public_key_format , enckey ,
551
+ dependencies , boot_record , custom_tlvs , compression_tlvs ,
552
+ compression , int (encrypt_keylen ), clear , baked_signature ,
553
+ pub_key , vector_to_sign )
554
+ img = compressed_img
489
555
img .save (outfile , hex_addr )
490
-
491
556
if sig_out is not None :
492
557
new_signature = img .get_signature ()
493
558
save_signature (sig_out , new_signature )
0 commit comments