123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967 |
- # Copyright(c) 2018 STMicroelectronics International N.V.
- # Copyright 2017 Linaro Limited
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- #
- import keys
- import sys
- import argparse
- import os
- import hashlib
- import numpy
- import elftools
- #import string
- from elftools.elf.elffile import ELFFile
- from struct import pack
- def gen_ecdsa_p256(args):
- keys.ECDSA256P1.generate().export_private(args.key)
- def gen_aes_gcm(args):
- keys.AES_GCM.generate().export_private(args.key)
- def gen_aes_cbc(args):
- keys.AES_CBC.generate().export_private(args.key)
- def gen_aes_ctr(args):
- keys.AES_CTR.generate().export_private(args.key)
- keygens = {
- 'aes-gcm': gen_aes_gcm,
- 'aes-cbc': gen_aes_cbc,
- 'aes-ctr': gen_aes_ctr,
- 'ecdsa-p256': gen_ecdsa_p256,
- }
- def do_keygen(args):
- if args.type not in keygens:
- msg = "Unexpected key type: {}".format(args.type)
- raise argparse.ArgumentTypeError(msg)
- keygens[args.type](args)
- def do_trans(args):
- key = keys.load(args.key)
- end = False
- if args.end:
- end = True
- if args.assembly=="ARM" or args.assembly=="IAR" or args.assembly=="GNU":
- if args.version=="V6M" or args.version=="V7M":
- out = key.trans(args.section, args.function, end, args.assembly, args.version)
- print (str(out))
- else:
- print ("-v option : Cortex M architecture not supported")
- exit(1)
- else:
- print ("-a option : assembly option not supported")
- exit(1)
- def do_getpub(args):
- key = keys.load(args.key)
- key.emit_c()
- def do_sign(args):
- payload = []
- with open(args.infile, 'rb') as f:
- payload = f.read()
- # Removing the padding because the hashlib pads properly (512 bit alignment) for SHA256
- # payload = pad(payload)
- key = keys.load(args.key) if args.key else None
- if key.has_sign():
- if key.has_nonce():
- nonce = []
- try:
- with open(args.nonce, 'rb') as f:
- nonce = f.read()
- except:
- nonce= []
- encrytpted, signature , nonce_used = key.encrypt( payload, nonce)
- if nonce !=nonce_used:
- try:
- f = open(args.nonce, 'wb')
- f.write(nonce_used)
- f.close()
- except:
- print("nonce filename required")
- exit(1)
- else:
- signature = key.sign(payload)
- f = open(args.outfile,"wb")
- f.write(signature)
- f.close()
- else:
- print("Provided Key is not usable to sign ")
- exit(1)
- def do_sha(args):
- payload = []
- with open(args.infile, 'rb') as f:
- payload = f.read()
- m = hashlib.sha256()
- buffer=payload
- m.update(buffer)
- signature = m.digest()
- f = open(args.outfile, "wb")
- f.write(signature)
- f.close()
- def do_encrypt(args):
- payload = []
- with open(args.infile, 'rb') as f:
- payload = f.read()
- f.close()
- key = keys.load(args.key) if args.key else None
- if key.has_encrypt():
- if key.has_nonce():
- nonce = []
- if args.nonce and args.iv:
- print("either IV or Nonce Required for this key!!!")
- exit(1)
- if args.nonce:
- iv_nonce=args.nonce
- elif args.iv:
- iv_nonce=args.iv
- else:
- print("either IV or Nonce Required for this key!!!")
- exit(1)
- if os.path.isfile(iv_nonce):
- with open(iv_nonce, 'rb') as f:
- nonce = f.read()
- if args.poffset:
- with open(args.poffset, 'r') as f:
- offset = int(f.read())
- # AES CTR : remove 4 LSB bits to the address
- offset = int(offset / 16)
- else:
- offset = 0
- if args.address:
- address = args.address + offset
- encrypted , signature , nonce_used = key.encrypt(payload, address, nonce)
- else:
- encrypted , signature , nonce_used = key.encrypt(payload, nonce)
- if nonce !=nonce_used:
- f = open(iv_nonce, 'wb')
- f.write(nonce_used)
- f.close()
- else:
- encrypted ,signature = key.encrypt(payload)
- f=open(args.outfile,"wb")
- f.write(encrypted)
- f.close()
- else:
- print("Key does not support encrypt")
- exit(1)
- def do_header_lib(args):
- if (os.path.isfile(args.firmware)):
- size = os.path.getsize(args.firmware)
- else:
- print("no fw file")
- exit(1)
- if os.path.isfile(args.tag):
- f=open(args.tag, 'rb')
- tag = f.read()
- if args.cert_fw_leaf :
- if os.path.isfile(args.cert_fw_leaf):
- print("Loading cert: "+args.cert_fw_leaf)
- f=open(args.cert_fw_leaf, 'rb')
- cert_fw_leaf = f.read()
- if args.cert_fw_inter :
- if os.path.isfile(args.cert_fw_inter):
- print("Loading cert: "+args.cert_fw_inter)
- f=open(args.cert_fw_inter, 'rb')
- cert_fw_inter = f.read()
-
- key = keys.load(args.key) if args.key else None
- #empty nonce
- nonce = b''
- protocol = args.protocol
- magic = args.magic.encode()
- version = args.version
- reserved = b'\0'*args.reserved
- if args.nonce and args.iv:
- print("either IV or Nonce Required !!!")
- exit(1)
- iv_nonce=""
- if args.nonce:
- iv_nonce=args.nonce
- elif args.iv:
- iv_nonce=args.iv
- if iv_nonce:
- with open(iv_nonce, 'rb') as f:
- nonce = f.read()
- if args.pfw:
- pfwsize = os.path.getsize(args.pfw)
- else:
- pfwsize = size
- if args.poffset:
- with open(args.poffset, 'r') as f:
- pfwoffset = int(f.read())
- else:
- pfwoffset = 0
- if args.ptag:
- f=open(args.ptag, 'rb')
- pfwtag = f.read()
- f.close()
- else:
- pfwtag=tag
- print("Magic: "+str(magic)+"!!")
- header = pack('<'+str(len(magic))+'sHHIII'+str(len(tag))+'s'+str(len(pfwtag))+'s'+str(len(nonce))+'s'+str(args.reserved)+'s',
- magic, protocol, version, size, pfwoffset, pfwsize, tag, pfwtag, nonce, reserved)
- #add certs
- # Add certificates
- if args.cert_fw_leaf :
- print("adding leaf cert of length "+str(str(len(cert_fw_leaf))))
- header +=pack(str(len(cert_fw_leaf))+'s',cert_fw_leaf)
- if args.cert_fw_inter :
- print("adding intermediate cert of length "+str(str(len(cert_fw_inter))))
- header +=pack(str(len(cert_fw_inter))+'s',cert_fw_inter)
- # Pad, leaving 64 bytes for signature + 3*32 bytes for image state + 32 Fingerprint
- if (args.cert_fw_leaf or args.cert_fw_inter):
- if 'pack' in args.subcmd:
- padding = args.offset - (len(header) + 3*32 + 32 + 64)
- else:
- padding = args.offset - (len(header) + 3*32 + 32 + 64)
- if (padding<0):
- print("Invalid padding: "+str(padding))
- exit(1)
- else:
- print("Adding padding: "+str(padding))
- header += b'\0'*padding
- #GCM needs Nonce to sign
- #AES cannot sign
- if key.has_sign():
- if key.has_nonce() and iv_nonce=="":
- print("sign key required nonce, provide nonce")
- exit(1)
- if key.has_nonce():
- if nonce != b'':
- signature , nonce_used = key.sign(header, nonce)
- if nonce_used !=nonce:
- print("error nonce used differs")
- exit(1)
- else:
- print("nonce required for this key")
- exit(1)
- else:
- signature = key.sign(header)
- header +=pack(str(len(signature))+'s',signature)
- else:
- print("Provided Key is not usable to sign header")
- exit(1)
- if 'pack' in args.subcmd:
- # image state = new
- header += b'\xFF'*96
- else:
- # image state = valid
- header += b'\xFF'*32
- header += b'\x00'*64
- # UpdateSourceFingerprint - init to 0x00
- header += b'\x00'*32
- return header, signature
- #header is used only to build install header for merge .elf tool
- def do_header(args):
- header ,signature = do_header_lib(args)
- f=open(args.outfile,"wb")
- if len(signature) == 16:
- signature = 2*signature
- elif len(signature) == 64:
- signature = signature[0:32]
- else:
- print("Unexpected signature size : "+str(len(signature))+"!!")
- f.write(header)
- padding = args.offset - (len(header))
- while (padding != 0):
- f.write(b'\xff')
- padding = padding - 1
- f.close()
- def do_conf(args):
- if (os.path.isfile(args.infile)):
- f = open(args.infile)
- for myline in f:
- if args.define in myline:
- myword = myline.split()
- if myword[1] == args.define:
- print(myword[2])
- f.close()
- return
- f.close()
- print("#DEFINE "+args.define+" not found")
- exit(1)
- def do_extract(args):
- if (os.path.isfile(args.infile)):
- f = open(args.infile)
- for myline in f:
- if args.define in myline:
- myword = myline.split('0x')
- print('0x'+ myword[1][:8])
- f.close()
- return
- f.close()
- print(args.define+" not found")
- exit(1)
- def do_pack(args):
- header,signature = do_header_lib(args)
- f=open(args.outfile,"wb")
- f.write(header)
- if len(header) > args.offset:
- print("error header is larger than offset before binary")
- sys.exit(1)
- #create the string to padd
- tmp = (args.offset-len(header))*b'\xff'
- #write to file
- f.write(tmp)
- if args.pfw:
- #recopy encrypted partial file
- binary=open(args.pfw,'rb')
- else:
- #recopy encrypted complete file
- binary=open(args.firmware,'rb')
- tmp=binary.read()
- f.write(tmp)
- binary.close()
- #find lowest sction to fix base address not matching
- def find_lowest_section(elffile):
- lowest_addr = 0
- lowest_size = 0
- for s in elffile.iter_sections():
- if (s.header['sh_flags'] & elftools.elf.constants.SH_FLAGS.SHF_ALLOC) and (s.header.sh_type != 'SHT_NOBITS'):
- sh_addr = s.header['sh_addr'];
- if lowest_addr == 0:
- lowest_addr = sh_addr
- elif sh_addr < lowest_addr:
- lowest_addr = sh_addr
- lowest_size = s.header['sh_size']
- return lowest_addr, lowest_size
- #return base address of segment, and a binary array,
- #add padding pattern
- def get_binary(elffile,pad=0xff, elftype=0):
- num = elffile.num_segments()
- print("number of segment :"+str(num))
- for i in range(0,num):
- segment= elffile.get_segment(i)
- if segment['p_type'] == 'PT_LOAD':
- if i!=0:
- print(hex(nextaddress))
- if (len(segment.data())):
- padd_size=segment.__getitem__("p_paddr")- nextaddress
- binary+=padd_size*pack("B",pad)
- binary+=segment.data()
- nextaddress = segment.__getitem__("p_paddr") + len(segment.data())
- else:
- binary=segment.data()
- if elftype == 0:
- base_address = segment.__getitem__("p_paddr")
- else:
- base_address , lowest_size = find_lowest_section(elffile)
- offset = base_address - segment.__getitem__("p_paddr")
- binary = binary[offset:]
- nextaddress = base_address + len(binary)
- return binary, base_address
- def do_merge(args):
- #get the different element to compute the big binary
- if args.userapp:
- with open(args.userapp, 'rb') as f:
- # get the data
- my_elffile = ELFFile(f)
- appli_binary, appli_base = get_binary(my_elffile, args.value,args.elf)
- with open(args.sbsfu, 'rb') as f:
- my_elffile = ELFFile(f)
- sbsfu_binary, sbsfu_base = get_binary(my_elffile, args.value, args.elf)
- with open(args.install, 'rb') as f:
- header_binary = f.read()
- if args.loader:
- with open(args.loader, 'rb') as f:
- # get the data
- my_elffile = ELFFile(f)
- loader_binary, loader_base = get_binary(my_elffile, args.value,args.elf)
- #merge the three or four elements and padd in between , add some extra byte to
- #appli for aes cbc support
- address_just_after_sbsfu = len(sbsfu_binary)+sbsfu_base
- if args.loader:
- beginaddress_loader = loader_base
- address_just_after_loader = len(loader_binary)+loader_base
- if args.header:
- beginaddress_header = args.header
- else:
- beginaddress_header = appli_base - len(header_binary)
- if args.loader and (beginaddress_loader < address_just_after_sbsfu):
- print("sbsfu is too large to merge with loader !!")
- exit(2)
- elif args.loader and (beginaddress_header < address_just_after_loader):
- print("loader is too large to merge with appli !!")
- exit(3)
- else:
- print("Merging")
- print("SBSFU Base = "+hex(sbsfu_base))
- if args.loader:
- print("Loader Base = "+hex(loader_base))
- print("Writing header = "+hex(beginaddress_header))
- if args.userapp:
- print("APPLI Base = "+hex(appli_base))
- if args.loader:
- padd_before_loader = beginaddress_loader - address_just_after_sbsfu
- padd_before_header = beginaddress_header - address_just_after_loader
- big_binary = sbsfu_binary + padd_before_loader * pack("B",args.value)+ loader_binary + padd_before_header * pack("B",args.value) + header_binary + appli_binary
- elif args.header:
- if args.userapp:
- # header not contiguous : SBSFU + pad + header + pad + header(fake) + UserApp
- padd_before_header = beginaddress_header - address_just_after_sbsfu
- padd_before_appli = appli_base - len(header_binary) - padd_before_header - len(header_binary) - address_just_after_sbsfu
- big_binary = sbsfu_binary + padd_before_header * pack("B",args.value) + header_binary + padd_before_appli * pack("B",args.value) + header_binary + appli_binary
- else:
- # header not contiguous : SBSFU + header + SBSFU (loader part)
- offset_header = beginaddress_header - sbsfu_base
- big_binary = sbsfu_binary[:offset_header] + header_binary + sbsfu_binary[offset_header + len(header_binary):]
- else:
- padd_before_header = beginaddress_header - address_just_after_sbsfu
- big_binary = sbsfu_binary + padd_before_header * pack("B",args.value) + header_binary + appli_binary
-
- print("Writing to "+str(args.outfile)+" "+str(len(big_binary)))
- with open(args.outfile, 'wb') as f:
- f.write(big_binary)
- def sub_mergev2(args, bb_dict, bin, bin_base, bin_end):
- #print("Current BB : "+hex(bb_dict["bb_base"])+" > "+hex(bb_dict["bb_end"]))
- #print("Add segment: "+hex(bin_base)+" > "+hex(bin_end))
- # injection
- # 6 cases:
- # Elf is strictly inside BB range => First check there is no useful data in BB range that will be erased by elf file, then copy elf contents into BB
- if (bin_base > bb_dict["bb_base"]) and (bin_end < bb_dict["bb_end"]):
- # Check overlap data
- overlapzone = bb_dict["big_binary"][bin_base-bb_dict["bb_base"]+1:(bin_base-bb_dict["bb_base"])+len(bin)-1]
- for b in overlapzone:
- if b != args.value:
- print("ERROR: overlapped zone is not empty")
- exit(3)
- # Create new BB
- bb_dict["big_binary"] = bb_dict["big_binary"][:bin_base-bb_dict["bb_base"]] + bin + bb_dict["big_binary"][(bin_base-bb_dict["bb_base"])+len(bin):]
- # bb_dict["bb_base"] and bb_dict["bb_end"] do not change
- # Elf is before BB range => No issue, paste both with padding in between
- elif (bin_end <= bb_dict["bb_base"]):
- bb_dict["big_binary"] = bin + (bb_dict["bb_base"] - bin_end) * pack("B",args.value) + bb_dict["big_binary"]
- bb_dict["bb_base"] = bin_base
- # bb_dict["bb_end"] do not change
- # Elf is after BB range => No issue, paste both with padding in between
- elif (bin_base >= bb_dict["bb_end"]):
- bb_dict["big_binary"] = bb_dict["big_binary"] + (bin_base - bb_dict["bb_end"]) * pack("B",args.value) + bin
- bb_dict["bb_end"] = bin_end
- # bb_start do not change
- # Elf starts before BB start and ends inside BB range => !!! abnormal case not supported !!!
- elif (bin_base <= bb_dict["bb_base"]) and (bin_end <= bb_dict["bb_end"]):
- print("ERROR current binary is overlapping with previously aggregated binaries !!")
- exit(2)
- # Elf starts inside BB range and ends after BB end => !!! abnormal case not supported !!!
- elif (bin_base >= bb_dict["bb_base"]) and (bin_end >= bb_dict["bb_end"]):
- print("ERROR current binary is overlapping with previously aggregated binaries !!")
- exit(2)
- # Elf starts before BB start and ends after BB end => First check there is no useful data in ELF file range that will be erased by BB, then copy BB contents into elf file binary that becomes new BB
- elif (bin_base < bb_dict["bb_base"]) and (bin_end > bb_dict["bb_end"]):
- # Check overlap data
- overlapzone = bin[bb_dict["bb_base"]-bin_base+1:(bb_dict["bb_base"]-bin_base)+len(bb_dict["big_binary"])-1]
- for b in overlapzone:
- if b != args.value:
- print("ERROR: overlapped zone is not empty")
- exit(3)
- # Create new BB
- bb_dict["big_binary"] = bin[:bb_dict["bb_base"]-bin_base] + bb_dict["big_binary"] + bin[(bb_dict["bb_base"]-bin_base)+len(bb_dict["big_binary"]):]
- bb_dict["bb_base"] = bin_base
- bb_dict["bb_end"] = bin_end
- else:
- print("ERROR segment configuration is generating not managed case !!")
- exit(99)
- def do_mergev2(args):
- bb_dict=dict()
- # loop on elf files
- if args.files:
- elffiles = args.files.split(';')
- for ef in elffiles:
- with open(ef, 'rb') as f:
- # get the data
- my_elffile = ELFFile(f)
- elf_binary, elf_base = get_binary(my_elffile, args.value,args.elf)
- elf_end = elf_base + len(elf_binary)
- print("Adding ELF file: "+ef+" from "+hex(elf_base)+" to "+hex(elf_end))
- # check if first file or injection needed
- if 'big_binary' in bb_dict:
- sub_mergev2(args, bb_dict, elf_binary, elf_base, elf_end)
- else:
- # first copy
- bb_dict['big_binary'] = elf_binary
- bb_dict['bb_base'] = elf_base
- bb_dict['bb_end'] = elf_end
- # loop on binary files
- binfiles = args.binaries.split(';')
- for bf in binfiles:
- #print(bf)
- _bf, _add = bf.split('@')
- with open(_bf, 'rb') as f:
- # get the data
- bin_binary = f.read()
- bin_base = auto_int(_add) & 0xffffffff
- bin_end = bin_base + len(bin_binary)
- print("Adding BIN file: "+_bf+" from "+hex(bin_base)+" to "+hex(bin_end))
- # check if first file or injection needed
- if 'big_binary' in bb_dict:
- sub_mergev2(args, bb_dict, bin_binary, bin_base, bin_end)
- else:
- # first copy
- bb_dict['big_binary'] = bin_binary
- bb_dict['bb_base'] = bin_base
- bb_dict['bb_end'] = bin_end
- print("Writing to "+str(args.outfile)+" "+str(len(bb_dict['big_binary'])))
- with open(args.outfile, 'wb') as f:
- f.write(bb_dict['big_binary'])
- with open(args.outfile+".baseadd", 'w') as f:
- f.write(hex(bb_dict['bb_base']))
- def do_append(args):
- #get the different element to compute the binary
- with open(args.userapp, 'rb') as f:
- my_elffile = ELFFile(f)
- appli_binary, appli_base = get_binary(my_elffile, args.value,args.elf)
- with open(args.binary, 'rb') as f:
- input_binary = f.read()
- with open(args.install, 'rb') as f:
- header_binary = f.read()
- #Add header + user application to the existing binary and padd in between
- input_binary_base = args.address
- address_just_after_input_binary = len(input_binary) + input_binary_base
- if args.header:
- beginaddress_header = args.header
- else:
- beginaddress_header = appli_base - len(header_binary)
- print("Merging")
- print("Binary base = "+hex(input_binary_base))
- print("Writing header = "+hex(beginaddress_header))
- print("APPLI base = "+hex(appli_base))
- if beginaddress_header < address_just_after_input_binary:
- # header not contiguous : input binary + header + input binary (remaining part) + pad + header(fake) + UserApp
- padd_before_appli = appli_base - len(header_binary) - address_just_after_input_binary
- offset_header = beginaddress_header - input_binary_base
- output_binary = input_binary[:offset_header] + header_binary + input_binary[offset_header + len(header_binary):] + padd_before_appli * pack("B",args.value) + header_binary + appli_binary
- else:
- # header not contiguous : input binary + pad + header + UserApp
- padd_before_appli = appli_base - len(header_binary) - address_just_after_input_binary
- output_binary = input_binary + + padd_before_appli * pack("B",args.value) + header_binary + appli_binary
- print("Writing to "+str(args.outfile)+" "+str(len(output_binary)))
- with open(args.outfile, 'wb') as f:
- f.write(output_binary)
- def do_diff(args):
- # Check args
- if args.align == 0:
- msg = "Wrong alignment value, must be greater than 0"
- raise argparse.ArgumentTypeError(msg)
- if args.begin % args.align > 0:
- msg = "Wrong begin value ({}), must be a modulo of the specified alignment ({})".format(args.begin, args.align)
- raise argparse.ArgumentTypeError(msg)
- if args.end > 0 and (args.end + 1) % args.align > 0:
- msg = "Wrong end value ({}), must be last offset of a block defined by alignment ({})".format(args.end, args.align)
- raise argparse.ArgumentTypeError(msg)
- # Append the file 1 to get a size modulo alignment
- arr1 = numpy.fromfile(args.file1, numpy.int8)
- size = args.align - (arr1.size % args.align)
- for ite in range(0, size):
- arr1 = numpy.append(arr1, [0])
- arr1 = arr1.reshape(-1, args.align)
- # Append the file 2 to get a size modulo alignment
- arr2 = numpy.fromfile(args.file2, numpy.int8)
- arr2size = arr2.size
- size = args.align - (arr2.size % args.align)
- for ite in range(0, size):
- arr2 = numpy.append(arr2, [0])
- arr2 = arr2.reshape(-1, args.align)
-
- if args.end > 0 and args.end >= arr2.size:
- msg = "Wrong end set ({}), must be a within secondary binary file range ({})".format(args.end, arr2.size)
- raise argparse.ArgumentTypeError(msg)
-
- if arr1.size == arr2.size:
- # Both files to compare have same size
- if args.end > 0 and args.end < arr1.size:
- # args.end is specified within file size
- end_cmp = args.end
- else:
- # args.end is not specified or is out of file size
- end_cmp = arr1.size - 1
- # No additional data, copy stops at end of comparison
- end_cpy = -1
- elif arr1.size > arr2.size:
- # Primary file bigger than secondary
- if args.end > 0 and args.end < arr2.size:
- # args.end is specified within secondary file size
- end_cmp = args.end
- else:
- # args.end is not specified or is out of secondary file size
- end_cmp = arr2.size - 1
- # No additional data, copy stops at end of comparison
- end_cpy = -1
- else: # arr2.size > arr1.size
- if args.end > 0 and args.end < arr1.size:
- # args.end is specified within primary file size
- end_cmp = args.end
- # No additional data, copy stops at end of comparison
- end_cpy = -1
- else:
- # args.end is not specified or is out of primary file size
- end_cmp = arr1.size - 1
- if args.end > 0 and args.end < arr2.size:
- # Additional data to copy till specified args.end
- end_cpy = args.end
- else:
- # Additional data to copy till end of secondary file
- end_cpy = arr2.size - 1
- first_diff = -1
- last_diff = -1
- for ite in range(int(args.begin/args.align), int(end_cmp/args.align)+1):
- if not numpy.array_equal(arr1[ite], arr2[ite]):
- if first_diff == -1:
- first_diff = ite
- if end_cpy == -1:
- last_diff = ite
- else:
- # Additional data to copy, set last_diff to end of copy and exit loop
- last_diff = int(end_cpy/args.align)
- break
- else:
- last_diff = ite
-
- if first_diff == -1:
- if end_cpy == -1:
- sys.stderr.write("Input files are identical within comparison range ({},{})".format(args.begin, end_cmp))
- last_diff = -1
- first_diff = 0
- else:
- # No comparison differences detected, but additional data needs to be copied
- # Set first_diff & last_diff to first & last additional data to copy
- first_diff = int(arr1.size/args.align)
- last_diff = int(end_cpy/args.align)
-
- arrout = numpy.zeros((last_diff - first_diff + 1) * args.align, numpy.int8).reshape(-1, args.align)
- for ite in range(0, last_diff - first_diff + 1):
- arrout[ite] = arr2[first_diff + ite]
-
- # If last block concerned, remove the additional bytes appended at the beginning of the function
- arrout = arrout.reshape(1, -1)
- if ((last_diff + 1)* args.align) > arr2size :
- #last block is partial
- size = (last_diff - first_diff) * args.align + arr2size % args.align
- else:
- # all blocks are complete
- size = (last_diff - first_diff + 1) * args.align
- # keep only the useful part
- arrout = arrout[0,:size]
- # Store to outfile binary file
- arrout.tofile(args.outfile)
- # Store offset in file if specified
- with open(args.poffset, 'w') as f:
- f.write(str(first_diff*args.align))
- def do_inject(args):
- if args.key:
- key = keys.load(args.key)
- np_key = numpy.frombuffer(key.get_key(args.type), numpy.uint8)
- elif args.cert:
- np_key = numpy.fromfile(args.cert, numpy.uint8)
- else:
- raise argparse.ArgumentTypeError("Unknown error while parsing arguments")
- # DEBUG______________________________
- #print(key.get_key("public"))
- #l = list()
- #for ite in range(0, np_key.size):
- # l.append(np_key[ite])
- #print(l)
- # DEBUG______________________________
- with open(args.file, 'r') as f:
- with open(args.outfile, 'w') as o:
- for line in f.readlines():
- # DEBUG______________________________
- #print(line)
- # DEBUG______________________________
- if args.pattern in line:
- # DEBUG______________________________
- #print("Found:{}".format(line))
- # DEBUG______________________________
- if "CKA_VALUE" in line:
- value = "{},".format(np_key.size)
- for ite in range(0, int(np_key.size/4+0.75)): # int() truncate to integer part. +0,75 so that is reman of the division exist, it will be truncated to next int value
- if ite*4+3 < np_key.size:
- value += " 0x{:02x}{:02x}{:02x}{:02x}U,".format(np_key[ite*4],np_key[ite*4+1],np_key[ite*4+2],np_key[ite*4+3])
- elif ite*4+2 < np_key.size:
- value += " 0x{:02x}{:02x}{:02x}U,".format(np_key[ite*4],np_key[ite*4+1],np_key[ite*4+2])
- elif ite*4+1 < np_key.size:
- value += " 0x{:02x}{:02x}U,".format(np_key[ite*4],np_key[ite*4+1])
- else:
- value += " 0x{:02x}U,".format(np_key[ite*4])
- # DEBUG______________________________
- #print("Value:{}".format(value))
- # DEBUG______________________________
- line = line.replace(args.pattern, value)
- elif "CKA_EC_POINT" in line:
- np_key2 = numpy.zeros(np_key.size+3, numpy.uint8)
- for ite in range(0, np_key.size):
- np_key2[ite+3] = np_key[ite]
- np_key2[0] = 0x04; # Octet string
- np_key2[1] = np_key.size + 1; # Octet length
- np_key2[2] = 0x04; # X9.62 uncompressed format
- value = "{},".format(np_key2.size)
- for ite in range(0, int(np_key2.size/4+0.75)):
- if ite*4+3 < np_key2.size:
- value += " 0x{:02x}{:02x}{:02x}{:02x}U,".format(np_key2[ite*4],np_key2[ite*4+1],np_key2[ite*4+2],np_key2[ite*4+3])
- elif ite*4+2 < np_key2.size:
- value += " 0x{:02x}{:02x}{:02x}U,".format(np_key2[ite*4],np_key2[ite*4+1],np_key2[ite*4+2])
- elif ite*4+1 < np_key2.size:
- value += " 0x{:02x}{:02x}U,".format(np_key2[ite*4],np_key2[ite*4+1])
- else:
- value += " 0x{:02x}U,".format(np_key2[ite*4])
- # DEBUG______________________________
- #print("Value:{}".format(value))
- # DEBUG______________________________
- line = line.replace(args.pattern, value)
- # DEBUG______________________________
- #print("Altered line:{}".format(line))
- # DEBUG______________________________
- o.write(line)
- o.close()
- # end = False
- # if args.end:
- # end = True
- # if args.assembly=="ARM" or args.assembly=="IAR" or args.assembly=="GNU":
- # if args.version=="V6M" or args.version=="V7M":
- # out = key.trans(args.section, args.function, end, args.assembly, args.version)
- # print (str(out))
- # else:
- # print ("-v option : Cortex M architecture not supported")
- # exit(1)
- # else:
- # print ("-a option : assembly option not supported")
- # exit(1)
- subcmds = {
- 'keygen': do_keygen,
- 'trans':do_trans,
- 'getpub': do_getpub,
- #sign a binary with a givent key
- # nonce is required for aes gcm , if nonce not existing the file is
- # created
- # -k keyfilename -n nonce file
- 'sign': do_sign,
- #hash a file with sha256 and provide result in in a file
- 'sha256': do_sha,
- #return define value from crpto configuration from .h file
- #define to search default SECBOOT_CRYPTO_SCHEME, -d
- 'conf': do_conf,
- #define to search , -d
- 'extract': do_extract,
- #encrypt binary file with provided key
- # -k -n
- 'enc': do_encrypt,
- # give what to put in header and provide the key to compute hmac
- # magic (4 bytes) required, -m
- # protocol version(2 bytes) required , -p
- # nonce optional , -n
- # fwversion (required) 2 bytes, -ver
- # fw file (to get the size)
- # fw tag (file)
- # reserved size
- # key
- # offset default 512
- 'header':do_header,
- # give what to pack a single file header
- # magic (4 bytes) required, -m
- # protocol version(2 bytes) required , -p
- # nonce optional , -n
- # fwversion (required) 2 bytes, -ver
- # fw file (to get the size)
- # fw tag (file)
- # reserved size
- # key
- # offset default 512
- #
- 'pack':do_pack,
- #
- 'diff':do_diff,
- #merge appli.elf , header binary and sbsfu elf in a big binary
- #input file appli.elf
- #-h header file
- #-s sbsfu.elf
- #output file binary to merge
- #-v byte pattern to fill between the different segment default 0xff
- #-p padding length to add to appli binary
- 'merge':do_merge,
- 'mergev2':do_mergev2,
- 'diff':do_diff,
- #append appli.elf and header binary to an existing big binary
- 'append':do_append,
- #inject key into file by replacing pattern
- 'inject':do_inject,
- }
- def auto_int(x):
- if x.startswith("0x"):
- return int(x,16)
- else:
- return int(x)
- def args():
- parser = argparse.ArgumentParser()
- subs = parser.add_subparsers(help='subcommand help', dest='subcmd')
- keygenp = subs.add_parser('keygen', help='Generate pub/private keypair')
- keygenp.add_argument('-k', '--key', metavar='filename', required=True)
- keygenp.add_argument('-t', '--type', metavar='type',
- choices=['aes-gcm', 'ecdsa-p256','aes-cbc','aes-ctr'],
- required=True)
- trans = subs.add_parser('trans', help='translate key to execute only code')
- trans.add_argument('-k', '--key', metavar='filename', required=True)
- trans.add_argument('-f', '--function', type=str, required = True)
- trans.add_argument('-s', '--section', type=str, default="")
- trans.add_argument('-a', '--assembly',help='fix assembly type IAR or ARM or GNU', type=str,required = False, default="IAR")
- trans.add_argument('-v', '--version',help='fix CORTEX M architecture', type=str,required = False, default="V7M")
- trans.add_argument('-e', '--end')
- getpub = subs.add_parser('getpub', help='Get public key from keypair')
- getpub.add_argument('-k', '--key', metavar='filename', required=True)
- sign = subs.add_parser('sign', help='Sign an image with a private key')
- sign.add_argument('-k', '--key', metavar='filename', required = True)
- sign.add_argument('-n', '--nonce', metavar='filename', required = False)
- sign.add_argument("infile")
- sign.add_argument("outfile")
- sha = subs.add_parser('sha256', help='hash a file with sha256')
- sha.add_argument("infile")
- sha.add_argument("outfile")
- sha.add_argument('-p', '--padding',type=int, required = False, default=0, help='pad to be a multiple of the given size if needed')
- config = subs.add_parser('conf', help='get crypto config from .h file')
- config.add_argument('-d', '--define', type=str, default='SECBOOT_CRYPTO_SCHEME')
- config.add_argument("infile")
- extract = subs.add_parser('extract', help='get value definition from text file')
- extract.add_argument('-d', '--define', type=str, help='defintion searched in file')
- extract.add_argument("infile")
- enc = subs.add_parser('enc', help='encrypt an image with a private key')
- enc.add_argument('-k', '--key', metavar='filename', required = True)
- enc.add_argument('-n', '--nonce', metavar='filename')
- enc.add_argument('-i', '--iv', metavar='filename')
- enc.add_argument('-a', '--address', type=auto_int, help='part of IV for AES CTR. address = Slot0_FW_Start_Address[31:4]')
- enc.add_argument('--poffset', help ='file that contains offset at which the partial firmware should be applied', type=str, metavar='filename')
- enc.add_argument("infile")
- enc.add_argument("outfile")
- head = subs.add_parser('header', help='build installed header file and compute mac according to provided key')
- head.add_argument('-k', '--key', metavar='filename', required = True)
- head.add_argument('-n', '--nonce', metavar='filename')
- head.add_argument('-i', '--iv', metavar='filename')
- head.add_argument('-f', '--firmware', metavar='filename', required = True)
- head.add_argument('-t', '--tag', metavar='filename', required = True)
- head.add_argument('-v', '--version',type=int, required = True)
- head.add_argument('-m', '--magic',type=str, default="SFUM")
- head.add_argument('-p', '--protocol',type=int, default = 0x1)
- head.add_argument('-r', '--reserved',type=int, default=8)
- head.add_argument('-o', '--offset', type=int, default = 512, required = False)
- head.add_argument('--cert_fw_leaf', metavar='filename', required = False)
- head.add_argument('--cert_fw_inter', metavar='filename', required = False)
- head.add_argument('--pfw', help ='partial firmware', metavar='filename', required = ('--poffset' in sys.argv) or ('--ptag' in sys.argv))
- head.add_argument('--poffset', help ='file that contains offset at which the partial firmware should be applied', type=str, metavar='filename', required = ('--pfw' in sys.argv) or ('--ptag' in sys.argv))
- head.add_argument('--ptag', metavar='filename', required = ('--pfw' in sys.argv) or ('--poffset' in sys.argv))
- head.add_argument("outfile")
- pack = subs.add_parser('pack', help='build header file and compute mac according to key provided')
- pack.add_argument('-k', '--key', metavar='filename', required = True)
- pack.add_argument('-n', '--nonce', metavar='filename')
- pack.add_argument('-i', '--iv', metavar='filename')
- pack.add_argument('-f', '--firmware', metavar='filename', required = True)
- pack.add_argument('-t', '--tag', metavar='filename', required = True)
- pack.add_argument('-v', '--version',type=int, required = True)
- pack.add_argument('-m', '--magic',type=str, default="SFUM")
- pack.add_argument('-p', '--protocol',type=int, default = 0x1)
- pack.add_argument('-r', '--reserved',type=int, default=8)
- pack.add_argument('-o', '--offset', help='offset between start of header and binary', type=int, default=512)
- pack.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
- pack.add_argument('--cert_fw_leaf', metavar='filename', required = False)
- pack.add_argument('--cert_fw_inter', metavar='filename', required = False)
- pack.add_argument('--pfw', help ='partial firmware', metavar='filename', required = ('--poffset' in sys.argv) or ('--ptag' in sys.argv))
- pack.add_argument('--poffset', help ='file that contains offset at which the partial firmware should be applied', type=str, metavar='filename', required = ('--pfw' in sys.argv) or ('--ptag' in sys.argv))
- pack.add_argument('--ptag', metavar='filename', required = ('--pfw' in sys.argv) or ('--poffset' in sys.argv))
- pack.add_argument("outfile")
-
- diff = subs.add_parser('diff', help='compute differences between 2 binary files ')
- diff.add_argument('-1', '--file1', type=str, metavar='filename', required=True, help="first binary file to compare")
- diff.add_argument('-2', '--file2', type=str, metavar='filename', required=True, help="second binary file to compare")
- diff.add_argument('-p', '--poffset', type=str, metavar='filename', required=True, help="file that will contain offset at which the difference binary files should be applied")
- diff.add_argument('-b', '--begin', type=auto_int, metavar='filename', default='0x0', required=False, help="offset from which beginning comparison - in bytes (default: 0)")
- diff.add_argument('-e', '--end', type=auto_int, metavar='filename', default='0x0', required=False, help="offset at which ending comparison - in bytes. use 0 to specify end of given binary files (default: 0)")
- diff.add_argument('-a', '--align', type=int, metavar='align', default=2, required=False, help="difference binary file alignment in bytes (default: 2)")
- diff.add_argument("outfile")
-
- mrg = subs.add_parser('merge', help='merge elf appli , install header and sbsfu.elf in a contiguous binary')
- mrg.add_argument('-i', '--install', metavar='filename', help="filename of installed binary header", required = True)
- mrg.add_argument('-s', '--sbsfu', metavar='filename', help="filename of sbsfu elf", required = True)
- mrg.add_argument('-l', '--loader', metavar='filename', help="filename of loader elf", required = False)
- mrg.add_argument('-v', '--value', help= "byte padding pattern", required = False, type=int, default=0xff)
- mrg.add_argument('-p', '--padding', help='pad to add to appli binary, a multiple of the given size if needed',type=int, required = False, default=0)
- mrg.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
- mrg.add_argument('-x', '--header', type=auto_int, help='Header address, when header not contiguous with FW',required = False)
- mrg.add_argument('-u', '--userapp', metavar='filename', help="filename of appli elf file", required = False)
- mrg.add_argument("outfile", help = "filename of contiguous binary")
-
- mrgv2 = subs.add_parser('mergev2', help='merge elf files and binaries in a contiguous binary')
- mrgv2.add_argument('-b', '--binaries', type=str, help="binaries to merge files list (ex:'f1.bin@0x08004500;f1.bin@0x08006000'", required = True)
- mrgv2.add_argument('-f', '--files', type=str, help="elf files to merge list (ex:'f1.elf;f2.elf'", required = False)
- mrgv2.add_argument('-v', '--value', help= "byte padding pattern", required = False, type=int, default=0xff)
- mrgv2.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
- mrgv2.add_argument("outfile", help = "filename of contiguous binary")
- app = subs.add_parser('append', help='append elf appli and install header to an existing binary')
- app.add_argument('-i', '--install', metavar='filename', help="filename of installed binary header", required = True)
- app.add_argument('-b', '--binary', metavar='filename', help="filename of existing binary", required = True)
- app.add_argument('-v', '--value', help= "byte padding pattern", required = False, type=int, default=0xff)
- app.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
- app.add_argument('-x', '--header', type=auto_int, help='Header address',required = False)
- app.add_argument('-a', '--address', type=auto_int, help='Base address of input binary',required = False)
- app.add_argument('-u', '--userapp', metavar='filename', help="filename of user application elf file", required = False)
- app.add_argument("outfile", help = "filename of contiguous binary")
- inject = subs.add_parser('inject', help='inject key to KMS embedded keys code')
- inject.add_argument('-k', '--key', metavar='filename', required=('--cert' not in sys.argv) and ('-c' not in sys.argv))
- inject.add_argument('-c', '--cert', metavar='filename', help = "Certificate .der file", required=('--key' not in sys.argv) and ('-k' not in sys.argv))
- inject.add_argument('-p', '--pattern', type=str, required=True)
- inject.add_argument('-f', '--file', metavar='filename', required=True)
- inject.add_argument('-t', '--type', type=str, default="public")
- inject.add_argument("outfile", help = "generated output file")
- args = parser.parse_args()
- if args.subcmd is None:
- print('Must specify a subcommand')
- sys.exit(1)
- subcmds[args.subcmd](args)
- if __name__ == '__main__':
- args()
|