swupdate-common.bbclass 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. def swupdate_is_hash_needed(s, filename):
  2. with open(os.path.join(s, "sw-description"), 'r') as f:
  3. for line in f:
  4. if line.find("@%s" % (filename)) != -1:
  5. return True
  6. return False
  7. def swupdate_get_sha256(s, filename):
  8. import hashlib
  9. m = hashlib.sha256()
  10. with open(os.path.join(s, filename), 'rb') as f:
  11. while True:
  12. data = f.read(1024)
  13. if not data:
  14. break
  15. m.update(data)
  16. return m.hexdigest()
  17. def swupdate_extract_keys(keyfile_path):
  18. try:
  19. with open(keyfile_path, 'r') as f:
  20. lines = f.readlines()
  21. except IOError:
  22. bb.fatal("Failed to open file with keys %s" % (keyfile))
  23. data = {}
  24. for _ in lines:
  25. k,v = _.split('=',maxsplit=1)
  26. data[k.rstrip()] = v
  27. key = data['key'].rstrip('\n')
  28. iv = data['iv'].rstrip('\n')
  29. return key,iv
  30. def swupdate_encrypt_file(f, out, key, ivt):
  31. import subprocess
  32. encargs = ["openssl", "enc", "-aes-256-cbc", "-in", f, "-out", out]
  33. encargs += ["-K", key, "-iv", ivt, "-nosalt"]
  34. subprocess.run(encargs, check=True)
  35. def swupdate_write_sha256(s, filename, hash):
  36. write_lines = []
  37. with open(os.path.join(s, "sw-description"), 'r') as f:
  38. for line in f:
  39. write_lines.append(line.replace("@%s" % (filename), hash))
  40. with open(os.path.join(s, "sw-description"), 'w+') as f:
  41. for line in write_lines:
  42. f.write(line)
  43. def swupdate_expand_bitbake_variables(d, s):
  44. write_lines = []
  45. with open(os.path.join(s, "sw-description"), 'r') as f:
  46. import re
  47. for line in f:
  48. found = False
  49. while True:
  50. m = re.match(r"^(?P<before_placeholder>.+)@@(?P<bitbake_variable_name>\w+)@@(?P<after_placeholder>.+)$", line)
  51. if m:
  52. bitbake_variable_value = d.getVar(m.group('bitbake_variable_name'), True)
  53. if bitbake_variable_value is None:
  54. bitbake_variable_value = ""
  55. bb.warn("BitBake variable %s not set" % (m.group('bitbake_variable_name')))
  56. line = m.group('before_placeholder') + bitbake_variable_value + m.group('after_placeholder')
  57. found = True
  58. continue
  59. else:
  60. m = re.match(r"^(?P<before_placeholder>.+)@@(?P<bitbake_variable_name>.+)\[(?P<flag_var_name>.+)\]@@(?P<after_placeholder>.+)$", line)
  61. if m:
  62. bitbake_variable_value = (d.getVarFlag(m.group('bitbake_variable_name'), m.group('flag_var_name'), True) or "")
  63. if bitbake_variable_value is None:
  64. bitbake_variable_value = ""
  65. line = m.group('before_placeholder') + bitbake_variable_value + m.group('after_placeholder')
  66. continue
  67. if found:
  68. line = line + "\n"
  69. break
  70. write_lines.append(line)
  71. with open(os.path.join(s, "sw-description"), 'w+') as f:
  72. for line in write_lines:
  73. f.write(line)
  74. def swupdate_expand_auto_versions(d, s, list_for_cpio):
  75. import re
  76. import oe.packagedata
  77. AUTO_VERSION_TAG = "@SWU_AUTO_VERSION"
  78. AUTOVERSION_REGEXP = "version\s*=\s*\"%s" % AUTO_VERSION_TAG
  79. with open(os.path.join(s, "sw-description"), 'r') as f:
  80. data = f.read()
  81. def get_package_name(group, file_list):
  82. package = None
  83. m = re.search(r"%s:(?P<package>.+?(?=[\"@]))" % (AUTOVERSION_REGEXP), group)
  84. if m:
  85. package = m.group('package')
  86. return (package, True)
  87. for filename in file_list:
  88. if filename in group:
  89. package = filename
  90. if not package:
  91. bb.fatal("Failed to find file in group %s" % (group))
  92. return (package, False)
  93. def get_packagedata_key(group):
  94. m = re.search(r"%s.+?(?<=@)(?P<key>.+?(?=\"))" % (AUTOVERSION_REGEXP), group)
  95. if m:
  96. return (m.group('key'), True)
  97. return ("PV", False)
  98. regexp = re.compile(r"\{[^\{]*%s.[^\}]*\}" % (AUTOVERSION_REGEXP))
  99. while True:
  100. m = regexp.search(data)
  101. if not m:
  102. break
  103. group = data[m.start():m.end()]
  104. (package, pkg_name_defined) = get_package_name(group, list_for_cpio)
  105. pkg_info = os.path.join(d.getVar('PKGDATA_DIR'), 'runtime-reverse', package)
  106. pkgdata = oe.packagedata.read_pkgdatafile(pkg_info)
  107. (key, key_defined) = get_packagedata_key(group)
  108. if not key in pkgdata.keys():
  109. bb.warn("\"%s\" not set for package %s - using \"1.0\"" % (key, package))
  110. version = "1.0"
  111. else:
  112. version = pkgdata[key].split('+')[0]
  113. replace_str = AUTO_VERSION_TAG
  114. if pkg_name_defined:
  115. replace_str = replace_str + ":" + package
  116. if key_defined:
  117. replace_str = replace_str + "@" + key
  118. group = group.replace(replace_str, version)
  119. data = data[:m.start()] + group + data[m.end():]
  120. with open(os.path.join(s, "sw-description"), 'w+') as f:
  121. f.write(data)
  122. def prepare_sw_description(d, s, list_for_cpio):
  123. import shutil
  124. swupdate_expand_bitbake_variables(d, s)
  125. swupdate_expand_auto_versions(d, s, list_for_cpio)
  126. for file in list_for_cpio:
  127. if file != 'sw-description' and swupdate_is_hash_needed(s, file):
  128. hash = swupdate_get_sha256(s, file)
  129. swupdate_write_sha256(s, file, hash)
  130. encrypt = d.getVar('SWUPDATE_ENCRYPT_SWDESC', True)
  131. if encrypt:
  132. bb.note("Encryption of sw-description")
  133. shutil.copyfile(os.path.join(s, 'sw-description'), os.path.join(s, 'sw-description.plain'))
  134. key,iv = swupdate_extract_keys(d.getVar('SWUPDATE_AES_FILE', True))
  135. swupdate_encrypt_file(os.path.join(s, 'sw-description.plain'), os.path.join(s, 'sw-description'), key, iv)
  136. signing = d.getVar('SWUPDATE_SIGNING', True)
  137. if signing == "1":
  138. bb.warn('SWUPDATE_SIGNING = "1" is deprecated, falling back to "RSA". It is advised to set it to "RSA" if using RSA signing.')
  139. signing = "RSA"
  140. if signing:
  141. if signing == "CUSTOM":
  142. sign_tool = d.getVar('SWUPDATE_SIGN_TOOL', True)
  143. if sign_tool:
  144. ret = os.system(sign_tool)
  145. if ret != 0:
  146. bb.fatal("Failed to sign with %s" % (sign_tool))
  147. else:
  148. bb.fatal("Custom SWUPDATE_SIGN_TOOL is not given")
  149. elif signing == "RSA":
  150. privkey = d.getVar('SWUPDATE_PRIVATE_KEY', True)
  151. if not privkey:
  152. bb.fatal("SWUPDATE_PRIVATE_KEY isn't set")
  153. if not os.path.exists(privkey):
  154. bb.fatal("SWUPDATE_PRIVATE_KEY %s doesn't exist" % (privkey))
  155. passout = d.getVar('SWUPDATE_PASSWORD_FILE', True)
  156. if passout:
  157. passout = "-passin file:'%s' " % (passout)
  158. else:
  159. passout = ""
  160. signcmd = "openssl dgst -sha256 -sign '%s' %s -out '%s' '%s'" % (
  161. privkey,
  162. passout,
  163. os.path.join(s, 'sw-description.sig'),
  164. os.path.join(s, 'sw-description.plain' if encrypt else 'sw-description'))
  165. if os.system(signcmd) != 0:
  166. bb.fatal("Failed to sign sw-description with %s" % (privkey))
  167. elif signing == "CMS":
  168. cms_cert = d.getVar('SWUPDATE_CMS_CERT', True)
  169. if not cms_cert:
  170. bb.fatal("SWUPDATE_CMS_CERT is not set")
  171. if not os.path.exists(cms_cert):
  172. bb.fatal("SWUPDATE_CMS_CERT %s doesn't exist" % (cms_cert))
  173. cms_key = d.getVar('SWUPDATE_CMS_KEY', True)
  174. if not cms_key:
  175. bb.fatal("SWUPDATE_CMS_KEY isn't set")
  176. if not os.path.exists(cms_key):
  177. bb.fatal("SWUPDATE_CMS_KEY %s doesn't exist" % (cms_key))
  178. passout = d.getVar('SWUPDATE_PASSWORD_FILE', True)
  179. if passout:
  180. passout = "-passin file:'%s' " % (passout)
  181. else:
  182. passout = ""
  183. signcmd = "openssl cms -sign -in '%s' -out '%s' -signer '%s' -inkey '%s' %s -outform DER -nosmimecap -binary" % (
  184. os.path.join(s, 'sw-description.plain' if encrypt else 'sw-description'),
  185. os.path.join(s, 'sw-description.sig'),
  186. cms_cert,
  187. cms_key,
  188. passout)
  189. if os.system(signcmd) != 0:
  190. bb.fatal("Failed to sign sw-description with %s" % (privkey))
  191. else:
  192. bb.fatal("Unrecognized SWUPDATE_SIGNING mechanism.");