pbp.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. """
  2. pbp is a tool that signs/re-signs bootloader and generate data for root public key authentication.
  3. """
  4. import os
  5. import shutil
  6. import argparse
  7. from lib import gfh
  8. from lib import cert
  9. def get_file_sizeb(file_path):
  10. """
  11. Get size of binary file
  12. """
  13. if not os.path.isfile(file_path):
  14. return 0
  15. file_handle = open(file_path, "rb")
  16. file_handle.seek(0, 2)
  17. file_size = file_handle.tell()
  18. file_handle.close()
  19. return file_size
  20. def concatb(file1_path, file2_path):
  21. """
  22. Concatenate binary files
  23. """
  24. file2_size = get_file_sizeb(file2_path)
  25. file1 = open(file1_path, "ab+")
  26. file2 = open(file2_path, "rb")
  27. file1.write(file2.read(file2_size))
  28. file2.close()
  29. file1.close()
  30. class Bl(object):
  31. """
  32. Bl, which stands for preloader in Mediatek solution.
  33. Mediatek preloader is loaded/verified by BootROM and its format is determined by BootROM
  34. and is different from other images due to several reasons.
  35. It has basic format as follows:
  36. =======================
  37. GFH
  38. =======================
  39. Preloader_NO_GFH.bin
  40. =======================
  41. Sig
  42. =======================
  43. Where Preloader_NO_GFH.bin is converted from preloader.elf.
  44. """
  45. def __init__(self, out_path, in_bootloader_file_path, out_bootloader_file_path):
  46. self.m_gfh = gfh.ImageGFH()
  47. self.m_out_path = out_path
  48. if not os.path.exists(self.m_out_path):
  49. os.makedirs(self.m_out_path)
  50. self.m_in_bl_file_path = in_bootloader_file_path
  51. self.m_out_bl_file_path = out_bootloader_file_path
  52. self.m_bl_is_signed = False
  53. self.m_bl_content_offset = 0
  54. # initialize content size to bl file size
  55. self.m_bl_content_length = get_file_sizeb(self.m_in_bl_file_path)
  56. self.m_bl_sig_size = 0
  57. # generate file path for bl without gfh and signature
  58. bl_path = os.path.splitext(in_bootloader_file_path)
  59. self.m_bl_no_gfh_file_path = bl_path[0] + "_plain.bin"
  60. self.m_sig_ver = 0
  61. self.m_sw_ver = 0
  62. self.m_root_prvk_path = ""
  63. self.m_img_prvk_path = ""
  64. self.m_ac_key = 0
  65. self.m_sig_handler = None
  66. def is_signed(self):
  67. """
  68. GFH and signature are added after bootloader image has been processed by pbp.
  69. We use this fact to determine whether bootloader image is signed.
  70. """
  71. if self.m_in_bl_file_path:
  72. bl_file = open(self.m_in_bl_file_path, "rb")
  73. gfh_hdr_obj = gfh.GFHHeader()
  74. gfh_hdr_size = gfh_hdr_obj.get_size()
  75. gfh_hdr_buf = bl_file.read(gfh_hdr_size)
  76. self.m_bl_is_signed = gfh_hdr_obj.is_gfh(gfh_hdr_buf)
  77. bl_file.close()
  78. return self.m_bl_is_signed
  79. def parse(self):
  80. """
  81. If image is signed, we remove GFH and signature. Removed GFH is parsed and
  82. stored. Stored GFH will be used later if GFH ini file is not given.
  83. """
  84. print "===parse bootloader==="
  85. # image will be decomposed if it's signed
  86. if self.is_signed():
  87. gfh_total_size = self.m_gfh.parse(self.m_in_bl_file_path)
  88. self.m_bl_content_offset = gfh_total_size
  89. self.m_bl_content_length -= gfh_total_size
  90. self.m_bl_content_length -= self.m_gfh.get_sig_size()
  91. self.m_bl_sig_size = self.m_gfh.get_sig_size()
  92. in_file = open(self.m_in_bl_file_path, "rb")
  93. out_file = open(self.m_bl_no_gfh_file_path, "wb")
  94. in_file.seek(self.m_bl_content_offset)
  95. out_file.write(in_file.read(self.m_bl_content_length))
  96. out_file.close()
  97. in_file.close()
  98. else:
  99. shutil.copyfile(self.m_in_bl_file_path, self.m_bl_no_gfh_file_path)
  100. print "bootloader content size = " + hex(self.m_bl_content_length)
  101. def create_gfh(self, gfh_config):
  102. """
  103. GFH creation. GFH may be created from parsed/stored GFH config or from GFH config file
  104. provided by user.
  105. """
  106. self.parse()
  107. if gfh_config:
  108. if self.is_signed():
  109. del self.m_gfh.gfhs[:]
  110. self.m_gfh.load_ini(gfh_config)
  111. elif not self.is_signed():
  112. print "GFH_CONFIG.ini does not exist!!"
  113. return -1
  114. # self.m_gfh.dump()
  115. return 0
  116. def sign(self, key_ini_path, key_cert_path, content_config_file_path):
  117. """
  118. Sign bootloader according to its signature type, which is stored in GFH.
  119. """
  120. self.m_gfh.finalize(self.m_bl_content_length, key_ini_path)
  121. # create tbs_bootloader.bin
  122. tbs_bl_file_path = os.path.join(self.m_out_path, "tbs_preloader.bin")
  123. tbs_bl_file = open(tbs_bl_file_path, "wb")
  124. tbs_bl_file.write(self.m_gfh.pack())
  125. bl_no_gfh_file = open(self.m_bl_no_gfh_file_path, "rb")
  126. tbs_bl_file.write(bl_no_gfh_file.read(self.m_bl_content_length))
  127. bl_no_gfh_file.close()
  128. tbs_bl_file.close()
  129. print "===sign==="
  130. if self.m_gfh.get_sig_type() == "CERT_CHAIN":
  131. self.m_sig_handler = cert.CertChainV2()
  132. # create key cert if key cert does not exist
  133. if key_cert_path == "":
  134. key_cert_path = os.path.join(self.m_out_path, "key_cert.bin")
  135. if not os.path.isfile(key_cert_path):
  136. key_cert_file_name = os.path.basename(os.path.abspath(key_cert_path))
  137. self.m_sig_handler.create_key_cert(key_ini_path,
  138. self.m_out_path,
  139. key_cert_file_name)
  140. key_cert_path = os.path.join(self.m_out_path, key_cert_file_name)
  141. else:
  142. self.m_sig_handler.set_key_cert(key_cert_path)
  143. # create content cert
  144. content_cert_name = "content_cert.bin"
  145. self.m_sig_handler.create_content_cert(content_config_file_path,
  146. tbs_bl_file_path,
  147. self.m_out_path,
  148. content_cert_name)
  149. # create final cert chain
  150. sig_name = "preloader.sig"
  151. sig_file_path = os.path.join(self.m_out_path, sig_name)
  152. self.m_sig_handler.output(self.m_out_path, sig_name)
  153. # output final cert chain size
  154. sig_size_name = "sig_size.txt"
  155. sig_size_file_path = os.path.join(self.m_out_path, sig_size_name)
  156. sig_size_file = open(sig_size_file_path, 'w')
  157. sig_size_file.write(hex(get_file_sizeb(sig_file_path)))
  158. sig_size_file.close()
  159. # create final preloader image
  160. if os.path.isfile(self.m_out_bl_file_path):
  161. os.remove(self.m_out_bl_file_path)
  162. concatb(self.m_out_bl_file_path, tbs_bl_file_path)
  163. concatb(self.m_out_bl_file_path, sig_file_path)
  164. # clean up
  165. os.remove(os.path.join(self.m_out_path, content_cert_name))
  166. elif self.m_gfh.get_sig_type() == "SINGLE_AND_PHASH":
  167. self.m_sig_handler = cert.SigSingleAndPhash(self.m_gfh.get_pad_type())
  168. self.m_sig_handler.set_out_path(self.m_out_path)
  169. self.m_sig_handler.create(key_ini_path, tbs_bl_file_path)
  170. # signature generation
  171. self.m_sig_handler.sign()
  172. sig_name = "preloader.sig"
  173. sig_file_path = os.path.join(self.m_out_path, sig_name)
  174. self.m_sig_handler.output(self.m_out_path, sig_name)
  175. # output signature size
  176. sig_size_name = "sig_size.txt"
  177. sig_size_file_path = os.path.join(self.m_out_path, sig_size_name)
  178. sig_size_file = open(sig_size_file_path, 'w')
  179. sig_size_file.write(hex(get_file_sizeb(sig_file_path)))
  180. sig_size_file.close()
  181. # create final preloader image
  182. if os.path.isfile(self.m_out_bl_file_path):
  183. os.remove(self.m_out_bl_file_path)
  184. concatb(self.m_out_bl_file_path, tbs_bl_file_path)
  185. concatb(self.m_out_bl_file_path, sig_file_path)
  186. else:
  187. print "unknown signature type"
  188. # clean up
  189. os.remove(self.m_bl_no_gfh_file_path)
  190. os.remove(tbs_bl_file_path)
  191. os.remove(sig_file_path)
  192. return
  193. class PbpArgs(object):
  194. """
  195. PbpArgs is used to pass parameter to pbp.
  196. This structure is both used when user executes this python script directly or imports
  197. this module and use exported method.
  198. """
  199. def __init__(self):
  200. self.op = None
  201. self.padding = None
  202. self.key_ini_path = None
  203. self.key_path = None
  204. self.gfh_cfg_ini_path = None
  205. self.cnt_cfg_ini_path = None
  206. self.key_cert_path = None
  207. self.input_bl_path = None
  208. self.tmp_output_path = None
  209. self.output_path = None
  210. def reset(self):
  211. self.__init__()
  212. def dump(self):
  213. """
  214. dump parameters.
  215. """
  216. f = lambda arg: 'Not Set' if arg is None else arg
  217. print "op = " + f(self.op)
  218. print "padding = " + f(self.padding)
  219. print "key_ini_path = " + f(self.key_ini_path)
  220. print "key_path = " + f(self.key_path)
  221. print "gfh_cfg_ini_path = " + f(self.gfh_cfg_ini_path)
  222. print "cnt_cfg_ini_path = " + f(self.cnt_cfg_ini_path)
  223. print "key_cert_path = " + f(self.key_cert_path)
  224. print "input_bl_path = " + f(self.input_bl_path)
  225. print "tmp_output_path = " + f(self.tmp_output_path)
  226. print "output_path = " + f(self.output_path)
  227. def _op_sign(args):
  228. """
  229. Sign/re-sign operation
  230. """
  231. bl_obj = Bl(args.tmp_output_path, args.input_bl_path, args.output_path)
  232. bl_obj.create_gfh(args.gfh_cfg_ini_path)
  233. bl_obj.sign(args.key_ini_path, args.key_cert_path, args.cnt_cfg_ini_path)
  234. return 0
  235. def _op_keybin(args):
  236. """
  237. Generate root key data structure for root public key authentication.
  238. """
  239. key = cert.CtKey(args.padding)
  240. key.create(args.key_path)
  241. key_bin = key.pack()
  242. out_file = open(args.output_path, "wb")
  243. out_file.write(key_bin)
  244. out_file.close()
  245. return 0
  246. def _op_keybin_pss(args):
  247. """
  248. Root key data structures are different for different padding. Here we handles pss padding.
  249. """
  250. args.padding = 'pss'
  251. return _op_keybin(args)
  252. def _op_keybin_legacy(args):
  253. """
  254. Root key data structures are different for different padding. Here we handles legacy padding.
  255. """
  256. args.padding = 'legacy'
  257. return _op_keybin(args)
  258. def _op_keyhash(args):
  259. """
  260. Generate hash of root key data structure, which is dependent on padding used.
  261. """
  262. key = cert.CtKey(args.padding)
  263. key.create(args.key_path)
  264. key_bin = key.pack()
  265. tmp_key_bin_path = os.path.join(args.tmp_output_path, "tmp_keybin.bin")
  266. out_file = open(tmp_key_bin_path, "wb")
  267. out_file.write(key_bin)
  268. out_file.close()
  269. cert.hash_gen(tmp_key_bin_path, args.output_path)
  270. os.remove(tmp_key_bin_path)
  271. return 0
  272. def _op_keyhash_pss(args):
  273. """
  274. Root key data struture hash for pss padding.
  275. """
  276. args.padding = 'pss'
  277. return _op_keyhash(args)
  278. def _op_keyhash_legacy(args):
  279. """
  280. Root key data struture hash for legacy padding.
  281. """
  282. args.padding = 'legacy'
  283. return _op_keyhash(args)
  284. def pbp_op(args):
  285. """
  286. Handles and dispatches all operations supported by pbp.
  287. """
  288. supported_ops = {
  289. 'sign': _op_sign,
  290. 'keybin_pss': _op_keybin_pss,
  291. 'keybin_legacy': _op_keybin_legacy,
  292. 'keyhash_pss': _op_keyhash_pss,
  293. 'keyhash_legacy': _op_keyhash_legacy
  294. }
  295. if args.output_path is None:
  296. print "output path is not given!"
  297. return -1
  298. if args.op is None:
  299. print "op is not given!"
  300. return -1
  301. if args.op == 'sign':
  302. if not args.input_bl_path:
  303. print "bootloader path is not given!"
  304. return -1
  305. if (args.key_ini_path is None) and (args.key_cert_path is None):
  306. print "key path is not given!"
  307. return -1
  308. else:
  309. if (args.key_ini_path is None) and (args.key_path is None):
  310. print "key path is not given!"
  311. return -1
  312. args.tmp_output_path = os.path.dirname(os.path.abspath(args.output_path))
  313. if not os.path.exists(args.tmp_output_path):
  314. os.makedirs(args.tmp_output_path)
  315. op_f = supported_ops.get(args.op)
  316. return op_f(args)
  317. def main():
  318. """
  319. Main function for pbp, which is used when pbp.py is executed directly.
  320. Note that we changed input bootloader parameter to -in_bl $BL_PATH.
  321. Please remember to add -in_bl if you're migrating from previous version.
  322. """
  323. parser = argparse.ArgumentParser(description='pbp tool for preloader gfh \
  324. creation/replacement and signing/re-signing')
  325. parser.add_argument('-i', dest='key_ini_path', help='key configuartion path')
  326. parser.add_argument('-j', dest='key_path', help='key path (with pem format)')
  327. parser.add_argument('-g', dest='gfh_cfg_ini_path', help='gfh(generaic file header) \
  328. configuration path')
  329. parser.add_argument('-c', dest='cnt_cfg_ini_path', help='content certificate \
  330. configuration path')
  331. parser.add_argument('-k', dest='key_cert_path', help='key certificate path')
  332. parser.add_argument('-func', dest='op', help='operation to be performed', required=True)
  333. parser.add_argument('-o', dest='output_path', help='output file path')
  334. parser.add_argument('input_bl_path', nargs='?', help='input file path')
  335. pbp_args = parser.parse_args()
  336. return pbp_op(pbp_args)
  337. if __name__ == '__main__':
  338. main()