prepareimage.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. # Copyright(c) 2018 STMicroelectronics International N.V.
  2. # Copyright 2017 Linaro Limited
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import keys
  17. import sys
  18. import argparse
  19. import os
  20. import hashlib
  21. import numpy
  22. import elftools
  23. #import string
  24. from elftools.elf.elffile import ELFFile
  25. from struct import pack
  26. def gen_ecdsa_p256(args):
  27. keys.ECDSA256P1.generate().export_private(args.key)
  28. def gen_aes_gcm(args):
  29. keys.AES_GCM.generate().export_private(args.key)
  30. def gen_aes_cbc(args):
  31. keys.AES_CBC.generate().export_private(args.key)
  32. def gen_aes_ctr(args):
  33. keys.AES_CTR.generate().export_private(args.key)
  34. keygens = {
  35. 'aes-gcm': gen_aes_gcm,
  36. 'aes-cbc': gen_aes_cbc,
  37. 'aes-ctr': gen_aes_ctr,
  38. 'ecdsa-p256': gen_ecdsa_p256,
  39. }
  40. def do_keygen(args):
  41. if args.type not in keygens:
  42. msg = "Unexpected key type: {}".format(args.type)
  43. raise argparse.ArgumentTypeError(msg)
  44. keygens[args.type](args)
  45. def do_trans(args):
  46. key = keys.load(args.key)
  47. end = False
  48. if args.end:
  49. end = True
  50. if args.assembly=="ARM" or args.assembly=="IAR" or args.assembly=="GNU":
  51. if args.version=="V6M" or args.version=="V7M":
  52. out = key.trans(args.section, args.function, end, args.assembly, args.version)
  53. print (str(out))
  54. else:
  55. print ("-v option : Cortex M architecture not supported")
  56. exit(1)
  57. else:
  58. print ("-a option : assembly option not supported")
  59. exit(1)
  60. def do_getpub(args):
  61. key = keys.load(args.key)
  62. key.emit_c()
  63. def do_sign(args):
  64. payload = []
  65. with open(args.infile, 'rb') as f:
  66. payload = f.read()
  67. # Removing the padding because the hashlib pads properly (512 bit alignment) for SHA256
  68. # payload = pad(payload)
  69. key = keys.load(args.key) if args.key else None
  70. if key.has_sign():
  71. if key.has_nonce():
  72. nonce = []
  73. try:
  74. with open(args.nonce, 'rb') as f:
  75. nonce = f.read()
  76. except:
  77. nonce= []
  78. encrytpted, signature , nonce_used = key.encrypt( payload, nonce)
  79. if nonce !=nonce_used:
  80. try:
  81. f = open(args.nonce, 'wb')
  82. f.write(nonce_used)
  83. f.close()
  84. except:
  85. print("nonce filename required")
  86. exit(1)
  87. else:
  88. signature = key.sign(payload)
  89. f = open(args.outfile,"wb")
  90. f.write(signature)
  91. f.close()
  92. else:
  93. print("Provided Key is not usable to sign ")
  94. exit(1)
  95. def do_sha(args):
  96. payload = []
  97. with open(args.infile, 'rb') as f:
  98. payload = f.read()
  99. m = hashlib.sha256()
  100. buffer=payload
  101. m.update(buffer)
  102. signature = m.digest()
  103. f = open(args.outfile, "wb")
  104. f.write(signature)
  105. f.close()
  106. def do_encrypt(args):
  107. payload = []
  108. with open(args.infile, 'rb') as f:
  109. payload = f.read()
  110. f.close()
  111. key = keys.load(args.key) if args.key else None
  112. if key.has_encrypt():
  113. if key.has_nonce():
  114. nonce = []
  115. if args.nonce and args.iv:
  116. print("either IV or Nonce Required for this key!!!")
  117. exit(1)
  118. if args.nonce:
  119. iv_nonce=args.nonce
  120. elif args.iv:
  121. iv_nonce=args.iv
  122. else:
  123. print("either IV or Nonce Required for this key!!!")
  124. exit(1)
  125. if os.path.isfile(iv_nonce):
  126. with open(iv_nonce, 'rb') as f:
  127. nonce = f.read()
  128. if args.poffset:
  129. with open(args.poffset, 'r') as f:
  130. offset = int(f.read())
  131. # AES CTR : remove 4 LSB bits to the address
  132. offset = int(offset / 16)
  133. else:
  134. offset = 0
  135. if args.address:
  136. address = args.address + offset
  137. encrypted , signature , nonce_used = key.encrypt(payload, address, nonce)
  138. else:
  139. encrypted , signature , nonce_used = key.encrypt(payload, nonce)
  140. if nonce !=nonce_used:
  141. f = open(iv_nonce, 'wb')
  142. f.write(nonce_used)
  143. f.close()
  144. else:
  145. encrypted ,signature = key.encrypt(payload)
  146. f=open(args.outfile,"wb")
  147. f.write(encrypted)
  148. f.close()
  149. else:
  150. print("Key does not support encrypt")
  151. exit(1)
  152. def do_header_lib(args):
  153. if (os.path.isfile(args.firmware)):
  154. size = os.path.getsize(args.firmware)
  155. else:
  156. print("no fw file")
  157. exit(1)
  158. if os.path.isfile(args.tag):
  159. f=open(args.tag, 'rb')
  160. tag = f.read()
  161. if args.cert_fw_leaf :
  162. if os.path.isfile(args.cert_fw_leaf):
  163. print("Loading cert: "+args.cert_fw_leaf)
  164. f=open(args.cert_fw_leaf, 'rb')
  165. cert_fw_leaf = f.read()
  166. if args.cert_fw_inter :
  167. if os.path.isfile(args.cert_fw_inter):
  168. print("Loading cert: "+args.cert_fw_inter)
  169. f=open(args.cert_fw_inter, 'rb')
  170. cert_fw_inter = f.read()
  171. key = keys.load(args.key) if args.key else None
  172. #empty nonce
  173. nonce = b''
  174. protocol = args.protocol
  175. magic = args.magic.encode()
  176. version = args.version
  177. reserved = b'\0'*args.reserved
  178. if args.nonce and args.iv:
  179. print("either IV or Nonce Required !!!")
  180. exit(1)
  181. iv_nonce=""
  182. if args.nonce:
  183. iv_nonce=args.nonce
  184. elif args.iv:
  185. iv_nonce=args.iv
  186. if iv_nonce:
  187. with open(iv_nonce, 'rb') as f:
  188. nonce = f.read()
  189. if args.pfw:
  190. pfwsize = os.path.getsize(args.pfw)
  191. else:
  192. pfwsize = size
  193. if args.poffset:
  194. with open(args.poffset, 'r') as f:
  195. pfwoffset = int(f.read())
  196. else:
  197. pfwoffset = 0
  198. if args.ptag:
  199. f=open(args.ptag, 'rb')
  200. pfwtag = f.read()
  201. f.close()
  202. else:
  203. pfwtag=tag
  204. print("Magic: "+str(magic)+"!!")
  205. header = pack('<'+str(len(magic))+'sHHIII'+str(len(tag))+'s'+str(len(pfwtag))+'s'+str(len(nonce))+'s'+str(args.reserved)+'s',
  206. magic, protocol, version, size, pfwoffset, pfwsize, tag, pfwtag, nonce, reserved)
  207. #add certs
  208. # Add certificates
  209. if args.cert_fw_leaf :
  210. print("adding leaf cert of length "+str(str(len(cert_fw_leaf))))
  211. header +=pack(str(len(cert_fw_leaf))+'s',cert_fw_leaf)
  212. if args.cert_fw_inter :
  213. print("adding intermediate cert of length "+str(str(len(cert_fw_inter))))
  214. header +=pack(str(len(cert_fw_inter))+'s',cert_fw_inter)
  215. # Pad, leaving 64 bytes for signature + 3*32 bytes for image state + 32 Fingerprint
  216. if (args.cert_fw_leaf or args.cert_fw_inter):
  217. if 'pack' in args.subcmd:
  218. padding = args.offset - (len(header) + 3*32 + 32 + 64)
  219. else:
  220. padding = args.offset - (len(header) + 3*32 + 32 + 64)
  221. if (padding<0):
  222. print("Invalid padding: "+str(padding))
  223. exit(1)
  224. else:
  225. print("Adding padding: "+str(padding))
  226. header += b'\0'*padding
  227. #GCM needs Nonce to sign
  228. #AES cannot sign
  229. if key.has_sign():
  230. if key.has_nonce() and iv_nonce=="":
  231. print("sign key required nonce, provide nonce")
  232. exit(1)
  233. if key.has_nonce():
  234. if nonce != b'':
  235. signature , nonce_used = key.sign(header, nonce)
  236. if nonce_used !=nonce:
  237. print("error nonce used differs")
  238. exit(1)
  239. else:
  240. print("nonce required for this key")
  241. exit(1)
  242. else:
  243. signature = key.sign(header)
  244. header +=pack(str(len(signature))+'s',signature)
  245. else:
  246. print("Provided Key is not usable to sign header")
  247. exit(1)
  248. if 'pack' in args.subcmd:
  249. # image state = new
  250. header += b'\xFF'*96
  251. else:
  252. # image state = valid
  253. header += b'\xFF'*32
  254. header += b'\x00'*64
  255. # UpdateSourceFingerprint - init to 0x00
  256. header += b'\x00'*32
  257. return header, signature
  258. #header is used only to build install header for merge .elf tool
  259. def do_header(args):
  260. header ,signature = do_header_lib(args)
  261. f=open(args.outfile,"wb")
  262. if len(signature) == 16:
  263. signature = 2*signature
  264. elif len(signature) == 64:
  265. signature = signature[0:32]
  266. else:
  267. print("Unexpected signature size : "+str(len(signature))+"!!")
  268. f.write(header)
  269. padding = args.offset - (len(header))
  270. while (padding != 0):
  271. f.write(b'\xff')
  272. padding = padding - 1
  273. f.close()
  274. def do_conf(args):
  275. if (os.path.isfile(args.infile)):
  276. f = open(args.infile)
  277. for myline in f:
  278. if args.define in myline:
  279. myword = myline.split()
  280. if myword[1] == args.define:
  281. print(myword[2])
  282. f.close()
  283. return
  284. f.close()
  285. print("#DEFINE "+args.define+" not found")
  286. exit(1)
  287. def do_extract(args):
  288. if (os.path.isfile(args.infile)):
  289. f = open(args.infile)
  290. for myline in f:
  291. if args.define in myline:
  292. myword = myline.split('0x')
  293. print('0x'+ myword[1][:8])
  294. f.close()
  295. return
  296. f.close()
  297. print(args.define+" not found")
  298. exit(1)
  299. def do_pack(args):
  300. header,signature = do_header_lib(args)
  301. f=open(args.outfile,"wb")
  302. f.write(header)
  303. if len(header) > args.offset:
  304. print("error header is larger than offset before binary")
  305. sys.exit(1)
  306. #create the string to padd
  307. tmp = (args.offset-len(header))*b'\xff'
  308. #write to file
  309. f.write(tmp)
  310. if args.pfw:
  311. #recopy encrypted partial file
  312. binary=open(args.pfw,'rb')
  313. else:
  314. #recopy encrypted complete file
  315. binary=open(args.firmware,'rb')
  316. tmp=binary.read()
  317. f.write(tmp)
  318. binary.close()
  319. #find lowest sction to fix base address not matching
  320. def find_lowest_section(elffile):
  321. lowest_addr = 0
  322. lowest_size = 0
  323. for s in elffile.iter_sections():
  324. if (s.header['sh_flags'] & elftools.elf.constants.SH_FLAGS.SHF_ALLOC) and (s.header.sh_type != 'SHT_NOBITS'):
  325. sh_addr = s.header['sh_addr'];
  326. if lowest_addr == 0:
  327. lowest_addr = sh_addr
  328. elif sh_addr < lowest_addr:
  329. lowest_addr = sh_addr
  330. lowest_size = s.header['sh_size']
  331. return lowest_addr, lowest_size
  332. #return base address of segment, and a binary array,
  333. #add padding pattern
  334. def get_binary(elffile,pad=0xff, elftype=0):
  335. num = elffile.num_segments()
  336. print("number of segment :"+str(num))
  337. for i in range(0,num):
  338. segment= elffile.get_segment(i)
  339. if segment['p_type'] == 'PT_LOAD':
  340. if i!=0:
  341. print(hex(nextaddress))
  342. if (len(segment.data())):
  343. padd_size=segment.__getitem__("p_paddr")- nextaddress
  344. binary+=padd_size*pack("B",pad)
  345. binary+=segment.data()
  346. nextaddress = segment.__getitem__("p_paddr") + len(segment.data())
  347. else:
  348. binary=segment.data()
  349. if elftype == 0:
  350. base_address = segment.__getitem__("p_paddr")
  351. else:
  352. base_address , lowest_size = find_lowest_section(elffile)
  353. offset = base_address - segment.__getitem__("p_paddr")
  354. binary = binary[offset:]
  355. nextaddress = base_address + len(binary)
  356. return binary, base_address
  357. def do_merge(args):
  358. #get the different element to compute the big binary
  359. if args.userapp:
  360. with open(args.userapp, 'rb') as f:
  361. # get the data
  362. my_elffile = ELFFile(f)
  363. appli_binary, appli_base = get_binary(my_elffile, args.value,args.elf)
  364. with open(args.sbsfu, 'rb') as f:
  365. my_elffile = ELFFile(f)
  366. sbsfu_binary, sbsfu_base = get_binary(my_elffile, args.value, args.elf)
  367. with open(args.install, 'rb') as f:
  368. header_binary = f.read()
  369. if args.loader:
  370. with open(args.loader, 'rb') as f:
  371. # get the data
  372. my_elffile = ELFFile(f)
  373. loader_binary, loader_base = get_binary(my_elffile, args.value,args.elf)
  374. #merge the three or four elements and padd in between , add some extra byte to
  375. #appli for aes cbc support
  376. address_just_after_sbsfu = len(sbsfu_binary)+sbsfu_base
  377. if args.loader:
  378. beginaddress_loader = loader_base
  379. address_just_after_loader = len(loader_binary)+loader_base
  380. if args.header:
  381. beginaddress_header = args.header
  382. else:
  383. beginaddress_header = appli_base - len(header_binary)
  384. if args.loader and (beginaddress_loader < address_just_after_sbsfu):
  385. print("sbsfu is too large to merge with loader !!")
  386. exit(2)
  387. elif args.loader and (beginaddress_header < address_just_after_loader):
  388. print("loader is too large to merge with appli !!")
  389. exit(3)
  390. else:
  391. print("Merging")
  392. print("SBSFU Base = "+hex(sbsfu_base))
  393. if args.loader:
  394. print("Loader Base = "+hex(loader_base))
  395. print("Writing header = "+hex(beginaddress_header))
  396. if args.userapp:
  397. print("APPLI Base = "+hex(appli_base))
  398. if args.loader:
  399. padd_before_loader = beginaddress_loader - address_just_after_sbsfu
  400. padd_before_header = beginaddress_header - address_just_after_loader
  401. big_binary = sbsfu_binary + padd_before_loader * pack("B",args.value)+ loader_binary + padd_before_header * pack("B",args.value) + header_binary + appli_binary
  402. elif args.header:
  403. if args.userapp:
  404. # header not contiguous : SBSFU + pad + header + pad + header(fake) + UserApp
  405. padd_before_header = beginaddress_header - address_just_after_sbsfu
  406. padd_before_appli = appli_base - len(header_binary) - padd_before_header - len(header_binary) - address_just_after_sbsfu
  407. big_binary = sbsfu_binary + padd_before_header * pack("B",args.value) + header_binary + padd_before_appli * pack("B",args.value) + header_binary + appli_binary
  408. else:
  409. # header not contiguous : SBSFU + header + SBSFU (loader part)
  410. offset_header = beginaddress_header - sbsfu_base
  411. big_binary = sbsfu_binary[:offset_header] + header_binary + sbsfu_binary[offset_header + len(header_binary):]
  412. else:
  413. padd_before_header = beginaddress_header - address_just_after_sbsfu
  414. big_binary = sbsfu_binary + padd_before_header * pack("B",args.value) + header_binary + appli_binary
  415. print("Writing to "+str(args.outfile)+" "+str(len(big_binary)))
  416. with open(args.outfile, 'wb') as f:
  417. f.write(big_binary)
  418. def sub_mergev2(args, bb_dict, bin, bin_base, bin_end):
  419. #print("Current BB : "+hex(bb_dict["bb_base"])+" > "+hex(bb_dict["bb_end"]))
  420. #print("Add segment: "+hex(bin_base)+" > "+hex(bin_end))
  421. # injection
  422. # 6 cases:
  423. # 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
  424. if (bin_base > bb_dict["bb_base"]) and (bin_end < bb_dict["bb_end"]):
  425. # Check overlap data
  426. overlapzone = bb_dict["big_binary"][bin_base-bb_dict["bb_base"]+1:(bin_base-bb_dict["bb_base"])+len(bin)-1]
  427. for b in overlapzone:
  428. if b != args.value:
  429. print("ERROR: overlapped zone is not empty")
  430. exit(3)
  431. # Create new BB
  432. 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):]
  433. # bb_dict["bb_base"] and bb_dict["bb_end"] do not change
  434. # Elf is before BB range => No issue, paste both with padding in between
  435. elif (bin_end <= bb_dict["bb_base"]):
  436. bb_dict["big_binary"] = bin + (bb_dict["bb_base"] - bin_end) * pack("B",args.value) + bb_dict["big_binary"]
  437. bb_dict["bb_base"] = bin_base
  438. # bb_dict["bb_end"] do not change
  439. # Elf is after BB range => No issue, paste both with padding in between
  440. elif (bin_base >= bb_dict["bb_end"]):
  441. bb_dict["big_binary"] = bb_dict["big_binary"] + (bin_base - bb_dict["bb_end"]) * pack("B",args.value) + bin
  442. bb_dict["bb_end"] = bin_end
  443. # bb_start do not change
  444. # Elf starts before BB start and ends inside BB range => !!! abnormal case not supported !!!
  445. elif (bin_base <= bb_dict["bb_base"]) and (bin_end <= bb_dict["bb_end"]):
  446. print("ERROR current binary is overlapping with previously aggregated binaries !!")
  447. exit(2)
  448. # Elf starts inside BB range and ends after BB end => !!! abnormal case not supported !!!
  449. elif (bin_base >= bb_dict["bb_base"]) and (bin_end >= bb_dict["bb_end"]):
  450. print("ERROR current binary is overlapping with previously aggregated binaries !!")
  451. exit(2)
  452. # 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
  453. elif (bin_base < bb_dict["bb_base"]) and (bin_end > bb_dict["bb_end"]):
  454. # Check overlap data
  455. overlapzone = bin[bb_dict["bb_base"]-bin_base+1:(bb_dict["bb_base"]-bin_base)+len(bb_dict["big_binary"])-1]
  456. for b in overlapzone:
  457. if b != args.value:
  458. print("ERROR: overlapped zone is not empty")
  459. exit(3)
  460. # Create new BB
  461. 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"]):]
  462. bb_dict["bb_base"] = bin_base
  463. bb_dict["bb_end"] = bin_end
  464. else:
  465. print("ERROR segment configuration is generating not managed case !!")
  466. exit(99)
  467. def do_mergev2(args):
  468. bb_dict=dict()
  469. # loop on elf files
  470. if args.files:
  471. elffiles = args.files.split(';')
  472. for ef in elffiles:
  473. with open(ef, 'rb') as f:
  474. # get the data
  475. my_elffile = ELFFile(f)
  476. elf_binary, elf_base = get_binary(my_elffile, args.value,args.elf)
  477. elf_end = elf_base + len(elf_binary)
  478. print("Adding ELF file: "+ef+" from "+hex(elf_base)+" to "+hex(elf_end))
  479. # check if first file or injection needed
  480. if 'big_binary' in bb_dict:
  481. sub_mergev2(args, bb_dict, elf_binary, elf_base, elf_end)
  482. else:
  483. # first copy
  484. bb_dict['big_binary'] = elf_binary
  485. bb_dict['bb_base'] = elf_base
  486. bb_dict['bb_end'] = elf_end
  487. # loop on binary files
  488. binfiles = args.binaries.split(';')
  489. for bf in binfiles:
  490. #print(bf)
  491. _bf, _add = bf.split('@')
  492. with open(_bf, 'rb') as f:
  493. # get the data
  494. bin_binary = f.read()
  495. bin_base = auto_int(_add) & 0xffffffff
  496. bin_end = bin_base + len(bin_binary)
  497. print("Adding BIN file: "+_bf+" from "+hex(bin_base)+" to "+hex(bin_end))
  498. # check if first file or injection needed
  499. if 'big_binary' in bb_dict:
  500. sub_mergev2(args, bb_dict, bin_binary, bin_base, bin_end)
  501. else:
  502. # first copy
  503. bb_dict['big_binary'] = bin_binary
  504. bb_dict['bb_base'] = bin_base
  505. bb_dict['bb_end'] = bin_end
  506. print("Writing to "+str(args.outfile)+" "+str(len(bb_dict['big_binary'])))
  507. with open(args.outfile, 'wb') as f:
  508. f.write(bb_dict['big_binary'])
  509. with open(args.outfile+".baseadd", 'w') as f:
  510. f.write(hex(bb_dict['bb_base']))
  511. def do_append(args):
  512. #get the different element to compute the binary
  513. with open(args.userapp, 'rb') as f:
  514. my_elffile = ELFFile(f)
  515. appli_binary, appli_base = get_binary(my_elffile, args.value,args.elf)
  516. with open(args.binary, 'rb') as f:
  517. input_binary = f.read()
  518. with open(args.install, 'rb') as f:
  519. header_binary = f.read()
  520. #Add header + user application to the existing binary and padd in between
  521. input_binary_base = args.address
  522. address_just_after_input_binary = len(input_binary) + input_binary_base
  523. if args.header:
  524. beginaddress_header = args.header
  525. else:
  526. beginaddress_header = appli_base - len(header_binary)
  527. print("Merging")
  528. print("Binary base = "+hex(input_binary_base))
  529. print("Writing header = "+hex(beginaddress_header))
  530. print("APPLI base = "+hex(appli_base))
  531. if beginaddress_header < address_just_after_input_binary:
  532. # header not contiguous : input binary + header + input binary (remaining part) + pad + header(fake) + UserApp
  533. padd_before_appli = appli_base - len(header_binary) - address_just_after_input_binary
  534. offset_header = beginaddress_header - input_binary_base
  535. 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
  536. else:
  537. # header not contiguous : input binary + pad + header + UserApp
  538. padd_before_appli = appli_base - len(header_binary) - address_just_after_input_binary
  539. output_binary = input_binary + + padd_before_appli * pack("B",args.value) + header_binary + appli_binary
  540. print("Writing to "+str(args.outfile)+" "+str(len(output_binary)))
  541. with open(args.outfile, 'wb') as f:
  542. f.write(output_binary)
  543. def do_diff(args):
  544. # Check args
  545. if args.align == 0:
  546. msg = "Wrong alignment value, must be greater than 0"
  547. raise argparse.ArgumentTypeError(msg)
  548. if args.begin % args.align > 0:
  549. msg = "Wrong begin value ({}), must be a modulo of the specified alignment ({})".format(args.begin, args.align)
  550. raise argparse.ArgumentTypeError(msg)
  551. if args.end > 0 and (args.end + 1) % args.align > 0:
  552. msg = "Wrong end value ({}), must be last offset of a block defined by alignment ({})".format(args.end, args.align)
  553. raise argparse.ArgumentTypeError(msg)
  554. # Append the file 1 to get a size modulo alignment
  555. arr1 = numpy.fromfile(args.file1, numpy.int8)
  556. size = args.align - (arr1.size % args.align)
  557. for ite in range(0, size):
  558. arr1 = numpy.append(arr1, [0])
  559. arr1 = arr1.reshape(-1, args.align)
  560. # Append the file 2 to get a size modulo alignment
  561. arr2 = numpy.fromfile(args.file2, numpy.int8)
  562. arr2size = arr2.size
  563. size = args.align - (arr2.size % args.align)
  564. for ite in range(0, size):
  565. arr2 = numpy.append(arr2, [0])
  566. arr2 = arr2.reshape(-1, args.align)
  567. if args.end > 0 and args.end >= arr2.size:
  568. msg = "Wrong end set ({}), must be a within secondary binary file range ({})".format(args.end, arr2.size)
  569. raise argparse.ArgumentTypeError(msg)
  570. if arr1.size == arr2.size:
  571. # Both files to compare have same size
  572. if args.end > 0 and args.end < arr1.size:
  573. # args.end is specified within file size
  574. end_cmp = args.end
  575. else:
  576. # args.end is not specified or is out of file size
  577. end_cmp = arr1.size - 1
  578. # No additional data, copy stops at end of comparison
  579. end_cpy = -1
  580. elif arr1.size > arr2.size:
  581. # Primary file bigger than secondary
  582. if args.end > 0 and args.end < arr2.size:
  583. # args.end is specified within secondary file size
  584. end_cmp = args.end
  585. else:
  586. # args.end is not specified or is out of secondary file size
  587. end_cmp = arr2.size - 1
  588. # No additional data, copy stops at end of comparison
  589. end_cpy = -1
  590. else: # arr2.size > arr1.size
  591. if args.end > 0 and args.end < arr1.size:
  592. # args.end is specified within primary file size
  593. end_cmp = args.end
  594. # No additional data, copy stops at end of comparison
  595. end_cpy = -1
  596. else:
  597. # args.end is not specified or is out of primary file size
  598. end_cmp = arr1.size - 1
  599. if args.end > 0 and args.end < arr2.size:
  600. # Additional data to copy till specified args.end
  601. end_cpy = args.end
  602. else:
  603. # Additional data to copy till end of secondary file
  604. end_cpy = arr2.size - 1
  605. first_diff = -1
  606. last_diff = -1
  607. for ite in range(int(args.begin/args.align), int(end_cmp/args.align)+1):
  608. if not numpy.array_equal(arr1[ite], arr2[ite]):
  609. if first_diff == -1:
  610. first_diff = ite
  611. if end_cpy == -1:
  612. last_diff = ite
  613. else:
  614. # Additional data to copy, set last_diff to end of copy and exit loop
  615. last_diff = int(end_cpy/args.align)
  616. break
  617. else:
  618. last_diff = ite
  619. if first_diff == -1:
  620. if end_cpy == -1:
  621. sys.stderr.write("Input files are identical within comparison range ({},{})".format(args.begin, end_cmp))
  622. last_diff = -1
  623. first_diff = 0
  624. else:
  625. # No comparison differences detected, but additional data needs to be copied
  626. # Set first_diff & last_diff to first & last additional data to copy
  627. first_diff = int(arr1.size/args.align)
  628. last_diff = int(end_cpy/args.align)
  629. arrout = numpy.zeros((last_diff - first_diff + 1) * args.align, numpy.int8).reshape(-1, args.align)
  630. for ite in range(0, last_diff - first_diff + 1):
  631. arrout[ite] = arr2[first_diff + ite]
  632. # If last block concerned, remove the additional bytes appended at the beginning of the function
  633. arrout = arrout.reshape(1, -1)
  634. if ((last_diff + 1)* args.align) > arr2size :
  635. #last block is partial
  636. size = (last_diff - first_diff) * args.align + arr2size % args.align
  637. else:
  638. # all blocks are complete
  639. size = (last_diff - first_diff + 1) * args.align
  640. # keep only the useful part
  641. arrout = arrout[0,:size]
  642. # Store to outfile binary file
  643. arrout.tofile(args.outfile)
  644. # Store offset in file if specified
  645. with open(args.poffset, 'w') as f:
  646. f.write(str(first_diff*args.align))
  647. def do_inject(args):
  648. if args.key:
  649. key = keys.load(args.key)
  650. np_key = numpy.frombuffer(key.get_key(args.type), numpy.uint8)
  651. elif args.cert:
  652. np_key = numpy.fromfile(args.cert, numpy.uint8)
  653. else:
  654. raise argparse.ArgumentTypeError("Unknown error while parsing arguments")
  655. # DEBUG______________________________
  656. #print(key.get_key("public"))
  657. #l = list()
  658. #for ite in range(0, np_key.size):
  659. # l.append(np_key[ite])
  660. #print(l)
  661. # DEBUG______________________________
  662. with open(args.file, 'r') as f:
  663. with open(args.outfile, 'w') as o:
  664. for line in f.readlines():
  665. # DEBUG______________________________
  666. #print(line)
  667. # DEBUG______________________________
  668. if args.pattern in line:
  669. # DEBUG______________________________
  670. #print("Found:{}".format(line))
  671. # DEBUG______________________________
  672. if "CKA_VALUE" in line:
  673. value = "{},".format(np_key.size)
  674. 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
  675. if ite*4+3 < np_key.size:
  676. 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])
  677. elif ite*4+2 < np_key.size:
  678. value += " 0x{:02x}{:02x}{:02x}U,".format(np_key[ite*4],np_key[ite*4+1],np_key[ite*4+2])
  679. elif ite*4+1 < np_key.size:
  680. value += " 0x{:02x}{:02x}U,".format(np_key[ite*4],np_key[ite*4+1])
  681. else:
  682. value += " 0x{:02x}U,".format(np_key[ite*4])
  683. # DEBUG______________________________
  684. #print("Value:{}".format(value))
  685. # DEBUG______________________________
  686. line = line.replace(args.pattern, value)
  687. elif "CKA_EC_POINT" in line:
  688. np_key2 = numpy.zeros(np_key.size+3, numpy.uint8)
  689. for ite in range(0, np_key.size):
  690. np_key2[ite+3] = np_key[ite]
  691. np_key2[0] = 0x04; # Octet string
  692. np_key2[1] = np_key.size + 1; # Octet length
  693. np_key2[2] = 0x04; # X9.62 uncompressed format
  694. value = "{},".format(np_key2.size)
  695. for ite in range(0, int(np_key2.size/4+0.75)):
  696. if ite*4+3 < np_key2.size:
  697. 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])
  698. elif ite*4+2 < np_key2.size:
  699. value += " 0x{:02x}{:02x}{:02x}U,".format(np_key2[ite*4],np_key2[ite*4+1],np_key2[ite*4+2])
  700. elif ite*4+1 < np_key2.size:
  701. value += " 0x{:02x}{:02x}U,".format(np_key2[ite*4],np_key2[ite*4+1])
  702. else:
  703. value += " 0x{:02x}U,".format(np_key2[ite*4])
  704. # DEBUG______________________________
  705. #print("Value:{}".format(value))
  706. # DEBUG______________________________
  707. line = line.replace(args.pattern, value)
  708. # DEBUG______________________________
  709. #print("Altered line:{}".format(line))
  710. # DEBUG______________________________
  711. o.write(line)
  712. o.close()
  713. # end = False
  714. # if args.end:
  715. # end = True
  716. # if args.assembly=="ARM" or args.assembly=="IAR" or args.assembly=="GNU":
  717. # if args.version=="V6M" or args.version=="V7M":
  718. # out = key.trans(args.section, args.function, end, args.assembly, args.version)
  719. # print (str(out))
  720. # else:
  721. # print ("-v option : Cortex M architecture not supported")
  722. # exit(1)
  723. # else:
  724. # print ("-a option : assembly option not supported")
  725. # exit(1)
  726. subcmds = {
  727. 'keygen': do_keygen,
  728. 'trans':do_trans,
  729. 'getpub': do_getpub,
  730. #sign a binary with a givent key
  731. # nonce is required for aes gcm , if nonce not existing the file is
  732. # created
  733. # -k keyfilename -n nonce file
  734. 'sign': do_sign,
  735. #hash a file with sha256 and provide result in in a file
  736. 'sha256': do_sha,
  737. #return define value from crpto configuration from .h file
  738. #define to search default SECBOOT_CRYPTO_SCHEME, -d
  739. 'conf': do_conf,
  740. #define to search , -d
  741. 'extract': do_extract,
  742. #encrypt binary file with provided key
  743. # -k -n
  744. 'enc': do_encrypt,
  745. # give what to put in header and provide the key to compute hmac
  746. # magic (4 bytes) required, -m
  747. # protocol version(2 bytes) required , -p
  748. # nonce optional , -n
  749. # fwversion (required) 2 bytes, -ver
  750. # fw file (to get the size)
  751. # fw tag (file)
  752. # reserved size
  753. # key
  754. # offset default 512
  755. 'header':do_header,
  756. # give what to pack a single file header
  757. # magic (4 bytes) required, -m
  758. # protocol version(2 bytes) required , -p
  759. # nonce optional , -n
  760. # fwversion (required) 2 bytes, -ver
  761. # fw file (to get the size)
  762. # fw tag (file)
  763. # reserved size
  764. # key
  765. # offset default 512
  766. #
  767. 'pack':do_pack,
  768. #
  769. 'diff':do_diff,
  770. #merge appli.elf , header binary and sbsfu elf in a big binary
  771. #input file appli.elf
  772. #-h header file
  773. #-s sbsfu.elf
  774. #output file binary to merge
  775. #-v byte pattern to fill between the different segment default 0xff
  776. #-p padding length to add to appli binary
  777. 'merge':do_merge,
  778. 'mergev2':do_mergev2,
  779. 'diff':do_diff,
  780. #append appli.elf and header binary to an existing big binary
  781. 'append':do_append,
  782. #inject key into file by replacing pattern
  783. 'inject':do_inject,
  784. }
  785. def auto_int(x):
  786. if x.startswith("0x"):
  787. return int(x,16)
  788. else:
  789. return int(x)
  790. def args():
  791. parser = argparse.ArgumentParser()
  792. subs = parser.add_subparsers(help='subcommand help', dest='subcmd')
  793. keygenp = subs.add_parser('keygen', help='Generate pub/private keypair')
  794. keygenp.add_argument('-k', '--key', metavar='filename', required=True)
  795. keygenp.add_argument('-t', '--type', metavar='type',
  796. choices=['aes-gcm', 'ecdsa-p256','aes-cbc','aes-ctr'],
  797. required=True)
  798. trans = subs.add_parser('trans', help='translate key to execute only code')
  799. trans.add_argument('-k', '--key', metavar='filename', required=True)
  800. trans.add_argument('-f', '--function', type=str, required = True)
  801. trans.add_argument('-s', '--section', type=str, default="")
  802. trans.add_argument('-a', '--assembly',help='fix assembly type IAR or ARM or GNU', type=str,required = False, default="IAR")
  803. trans.add_argument('-v', '--version',help='fix CORTEX M architecture', type=str,required = False, default="V7M")
  804. trans.add_argument('-e', '--end')
  805. getpub = subs.add_parser('getpub', help='Get public key from keypair')
  806. getpub.add_argument('-k', '--key', metavar='filename', required=True)
  807. sign = subs.add_parser('sign', help='Sign an image with a private key')
  808. sign.add_argument('-k', '--key', metavar='filename', required = True)
  809. sign.add_argument('-n', '--nonce', metavar='filename', required = False)
  810. sign.add_argument("infile")
  811. sign.add_argument("outfile")
  812. sha = subs.add_parser('sha256', help='hash a file with sha256')
  813. sha.add_argument("infile")
  814. sha.add_argument("outfile")
  815. sha.add_argument('-p', '--padding',type=int, required = False, default=0, help='pad to be a multiple of the given size if needed')
  816. config = subs.add_parser('conf', help='get crypto config from .h file')
  817. config.add_argument('-d', '--define', type=str, default='SECBOOT_CRYPTO_SCHEME')
  818. config.add_argument("infile")
  819. extract = subs.add_parser('extract', help='get value definition from text file')
  820. extract.add_argument('-d', '--define', type=str, help='defintion searched in file')
  821. extract.add_argument("infile")
  822. enc = subs.add_parser('enc', help='encrypt an image with a private key')
  823. enc.add_argument('-k', '--key', metavar='filename', required = True)
  824. enc.add_argument('-n', '--nonce', metavar='filename')
  825. enc.add_argument('-i', '--iv', metavar='filename')
  826. enc.add_argument('-a', '--address', type=auto_int, help='part of IV for AES CTR. address = Slot0_FW_Start_Address[31:4]')
  827. enc.add_argument('--poffset', help ='file that contains offset at which the partial firmware should be applied', type=str, metavar='filename')
  828. enc.add_argument("infile")
  829. enc.add_argument("outfile")
  830. head = subs.add_parser('header', help='build installed header file and compute mac according to provided key')
  831. head.add_argument('-k', '--key', metavar='filename', required = True)
  832. head.add_argument('-n', '--nonce', metavar='filename')
  833. head.add_argument('-i', '--iv', metavar='filename')
  834. head.add_argument('-f', '--firmware', metavar='filename', required = True)
  835. head.add_argument('-t', '--tag', metavar='filename', required = True)
  836. head.add_argument('-v', '--version',type=int, required = True)
  837. head.add_argument('-m', '--magic',type=str, default="SFUM")
  838. head.add_argument('-p', '--protocol',type=int, default = 0x1)
  839. head.add_argument('-r', '--reserved',type=int, default=8)
  840. head.add_argument('-o', '--offset', type=int, default = 512, required = False)
  841. head.add_argument('--cert_fw_leaf', metavar='filename', required = False)
  842. head.add_argument('--cert_fw_inter', metavar='filename', required = False)
  843. head.add_argument('--pfw', help ='partial firmware', metavar='filename', required = ('--poffset' in sys.argv) or ('--ptag' in sys.argv))
  844. 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))
  845. head.add_argument('--ptag', metavar='filename', required = ('--pfw' in sys.argv) or ('--poffset' in sys.argv))
  846. head.add_argument("outfile")
  847. pack = subs.add_parser('pack', help='build header file and compute mac according to key provided')
  848. pack.add_argument('-k', '--key', metavar='filename', required = True)
  849. pack.add_argument('-n', '--nonce', metavar='filename')
  850. pack.add_argument('-i', '--iv', metavar='filename')
  851. pack.add_argument('-f', '--firmware', metavar='filename', required = True)
  852. pack.add_argument('-t', '--tag', metavar='filename', required = True)
  853. pack.add_argument('-v', '--version',type=int, required = True)
  854. pack.add_argument('-m', '--magic',type=str, default="SFUM")
  855. pack.add_argument('-p', '--protocol',type=int, default = 0x1)
  856. pack.add_argument('-r', '--reserved',type=int, default=8)
  857. pack.add_argument('-o', '--offset', help='offset between start of header and binary', type=int, default=512)
  858. pack.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
  859. pack.add_argument('--cert_fw_leaf', metavar='filename', required = False)
  860. pack.add_argument('--cert_fw_inter', metavar='filename', required = False)
  861. pack.add_argument('--pfw', help ='partial firmware', metavar='filename', required = ('--poffset' in sys.argv) or ('--ptag' in sys.argv))
  862. 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))
  863. pack.add_argument('--ptag', metavar='filename', required = ('--pfw' in sys.argv) or ('--poffset' in sys.argv))
  864. pack.add_argument("outfile")
  865. diff = subs.add_parser('diff', help='compute differences between 2 binary files ')
  866. diff.add_argument('-1', '--file1', type=str, metavar='filename', required=True, help="first binary file to compare")
  867. diff.add_argument('-2', '--file2', type=str, metavar='filename', required=True, help="second binary file to compare")
  868. 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")
  869. diff.add_argument('-b', '--begin', type=auto_int, metavar='filename', default='0x0', required=False, help="offset from which beginning comparison - in bytes (default: 0)")
  870. 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)")
  871. diff.add_argument('-a', '--align', type=int, metavar='align', default=2, required=False, help="difference binary file alignment in bytes (default: 2)")
  872. diff.add_argument("outfile")
  873. mrg = subs.add_parser('merge', help='merge elf appli , install header and sbsfu.elf in a contiguous binary')
  874. mrg.add_argument('-i', '--install', metavar='filename', help="filename of installed binary header", required = True)
  875. mrg.add_argument('-s', '--sbsfu', metavar='filename', help="filename of sbsfu elf", required = True)
  876. mrg.add_argument('-l', '--loader', metavar='filename', help="filename of loader elf", required = False)
  877. mrg.add_argument('-v', '--value', help= "byte padding pattern", required = False, type=int, default=0xff)
  878. 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)
  879. mrg.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
  880. mrg.add_argument('-x', '--header', type=auto_int, help='Header address, when header not contiguous with FW',required = False)
  881. mrg.add_argument('-u', '--userapp', metavar='filename', help="filename of appli elf file", required = False)
  882. mrg.add_argument("outfile", help = "filename of contiguous binary")
  883. mrgv2 = subs.add_parser('mergev2', help='merge elf files and binaries in a contiguous binary')
  884. mrgv2.add_argument('-b', '--binaries', type=str, help="binaries to merge files list (ex:'f1.bin@0x08004500;f1.bin@0x08006000'", required = True)
  885. mrgv2.add_argument('-f', '--files', type=str, help="elf files to merge list (ex:'f1.elf;f2.elf'", required = False)
  886. mrgv2.add_argument('-v', '--value', help= "byte padding pattern", required = False, type=int, default=0xff)
  887. mrgv2.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
  888. mrgv2.add_argument("outfile", help = "filename of contiguous binary")
  889. app = subs.add_parser('append', help='append elf appli and install header to an existing binary')
  890. app.add_argument('-i', '--install', metavar='filename', help="filename of installed binary header", required = True)
  891. app.add_argument('-b', '--binary', metavar='filename', help="filename of existing binary", required = True)
  892. app.add_argument('-v', '--value', help= "byte padding pattern", required = False, type=int, default=0xff)
  893. app.add_argument('-e', '--elf', help='elf type set to 1 for GNU, 0 for other by default', type=int, default=1)
  894. app.add_argument('-x', '--header', type=auto_int, help='Header address',required = False)
  895. app.add_argument('-a', '--address', type=auto_int, help='Base address of input binary',required = False)
  896. app.add_argument('-u', '--userapp', metavar='filename', help="filename of user application elf file", required = False)
  897. app.add_argument("outfile", help = "filename of contiguous binary")
  898. inject = subs.add_parser('inject', help='inject key to KMS embedded keys code')
  899. inject.add_argument('-k', '--key', metavar='filename', required=('--cert' not in sys.argv) and ('-c' not in sys.argv))
  900. inject.add_argument('-c', '--cert', metavar='filename', help = "Certificate .der file", required=('--key' not in sys.argv) and ('-k' not in sys.argv))
  901. inject.add_argument('-p', '--pattern', type=str, required=True)
  902. inject.add_argument('-f', '--file', metavar='filename', required=True)
  903. inject.add_argument('-t', '--type', type=str, default="public")
  904. inject.add_argument("outfile", help = "generated output file")
  905. args = parser.parse_args()
  906. if args.subcmd is None:
  907. print('Must specify a subcommand')
  908. sys.exit(1)
  909. subcmds[args.subcmd](args)
  910. if __name__ == '__main__':
  911. args()