pal_sgx_sign.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. #!/usr/bin/env python3
  2. import argparse
  3. import datetime
  4. import functools
  5. import hashlib
  6. import os
  7. import struct
  8. import subprocess
  9. import sys
  10. sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
  11. import generated_offsets as offs # pylint: disable=import-error,wrong-import-position
  12. # pylint: enable=invalid-name
  13. # Default / Architectural Options
  14. ARCHITECTURE = "amd64"
  15. SSAFRAMESIZE = offs.PAGESIZE
  16. DEFAULT_ENCLAVE_SIZE = '256M'
  17. DEFAULT_THREAD_NUM = 4
  18. ENCLAVE_HEAP_MIN = offs.DEFAULT_HEAP_MIN
  19. # Utilities
  20. ZERO_PAGE = bytes(offs.PAGESIZE)
  21. def roundup(addr):
  22. remaining = addr % offs.PAGESIZE
  23. if remaining:
  24. return addr + (offs.PAGESIZE - remaining)
  25. return addr
  26. def rounddown(addr):
  27. return addr - addr % offs.PAGESIZE
  28. def parse_size(value):
  29. scale = 1
  30. if value.endswith("K"):
  31. scale = 1024
  32. if value.endswith("M"):
  33. scale = 1024 * 1024
  34. if value.endswith("G"):
  35. scale = 1024 * 1024 * 1024
  36. if scale != 1:
  37. value = value[:-1]
  38. return int(value, 0) * scale
  39. # Reading / Writing Manifests
  40. def read_manifest(filename):
  41. manifest = dict()
  42. manifest_layout = []
  43. with open(filename, "r") as file:
  44. for line in file:
  45. if line == "":
  46. manifest_layout.append((None, None))
  47. break
  48. pound = line.find("#")
  49. if pound != -1:
  50. comment = line[pound:].strip()
  51. line = line[:pound]
  52. else:
  53. comment = None
  54. line = line.strip()
  55. equal = line.find("=")
  56. if equal != -1:
  57. key = line[:equal].strip()
  58. manifest[key] = line[equal + 1:].strip()
  59. else:
  60. key = None
  61. manifest_layout.append((key, comment))
  62. return (manifest, manifest_layout)
  63. def exec_sig_manifest(args, manifest):
  64. if 'exec' not in args or args.get('depend'):
  65. if 'loader.exec' in manifest:
  66. args['exec'] = resolve_manifest_uri(args['manifest'],
  67. manifest['loader.exec'])
  68. if 'sgx.sigfile' in manifest:
  69. args['sigfile'] = resolve_uri(manifest['sgx.sigfile'],
  70. check_exist=False)
  71. else:
  72. sigfile = args['output']
  73. for ext in ['.manifest.sgx.d', '.manifest.sgx', '.manifest']:
  74. if sigfile.endswith(ext):
  75. sigfile = sigfile[:-len(ext)]
  76. break
  77. args['sigfile'] = sigfile + '.sig'
  78. manifest['sgx.sigfile'] = 'file:' + os.path.basename(args['sigfile'])
  79. if args.get('libpal', None) is None:
  80. if 'sgx.enclave_pal_file' in manifest:
  81. args['libpal'] = resolve_manifest_uri(args['manifest'],
  82. manifest['sgx.enclave_pal_file'])
  83. else:
  84. print("Either --libpal or sgx.enclave_pal_file must be given", file=sys.stderr)
  85. return 1
  86. return 0
  87. def output_manifest(filename, manifest, manifest_layout):
  88. with open(filename, 'w') as file:
  89. written = []
  90. file.write('# DO NOT MODIFY. THIS FILE WAS AUTO-GENERATED.\n\n')
  91. for (key, comment) in manifest_layout:
  92. line = ''
  93. if key is not None:
  94. line += key + ' = ' + manifest[key]
  95. written.append(key)
  96. if comment is not None:
  97. if line != '':
  98. line += ' '
  99. line += comment
  100. file.write(line)
  101. file.write('\n')
  102. file.write('\n')
  103. file.write('# Generated by Graphene\n')
  104. file.write('\n')
  105. for key in sorted(manifest):
  106. if key not in written:
  107. file.write("%s = %s\n" % (key, manifest[key]))
  108. # Loading Enclave Attributes
  109. def get_enclave_attributes(manifest):
  110. sgx_flags = {
  111. 'FLAG_DEBUG': struct.pack("<Q", offs.SGX_FLAGS_DEBUG),
  112. 'FLAG_MODE64BIT': struct.pack("<Q", offs.SGX_FLAGS_MODE64BIT),
  113. }
  114. sgx_xfrms = {
  115. 'XFRM_LEGACY': struct.pack("<Q", offs.SGX_XFRM_LEGACY),
  116. 'XFRM_AVX': struct.pack("<Q", offs.SGX_XFRM_AVX),
  117. 'XFRM_AVX512': struct.pack("<Q", offs.SGX_XFRM_AVX512),
  118. 'XFRM_MPX': struct.pack("<Q", offs.SGX_XFRM_MPX),
  119. }
  120. sgx_miscs = {
  121. 'MISC_EXINFO': struct.pack("<L", offs.SGX_MISCSELECT_EXINFO),
  122. }
  123. default_attributes = {
  124. 'FLAG_DEBUG',
  125. 'XFRM_LEGACY',
  126. }
  127. if ARCHITECTURE == 'amd64':
  128. default_attributes.add('FLAG_MODE64BIT')
  129. manifest_options = {
  130. 'debug': 'FLAG_DEBUG',
  131. 'require_avx': 'XFRM_AVX',
  132. 'require_avx512': 'XFRM_AVX512',
  133. 'require_mpx': 'XFRM_MPX',
  134. 'support_exinfo': 'MISC_EXINFO',
  135. }
  136. attributes = default_attributes
  137. for opt in manifest_options:
  138. key = 'sgx.' + opt
  139. if key in manifest:
  140. if manifest[key] == '1':
  141. attributes.add(manifest_options[opt])
  142. else:
  143. attributes.discard(manifest_options[opt])
  144. flags_raw = struct.pack("<Q", 0)
  145. xfrms_raw = struct.pack("<Q", 0)
  146. miscs_raw = struct.pack("<L", 0)
  147. for attr in attributes:
  148. if attr in sgx_flags:
  149. flags_raw = bytes([a | b for a, b in
  150. zip(flags_raw, sgx_flags[attr])])
  151. if attr in sgx_xfrms:
  152. xfrms_raw = bytes([a | b for a, b in
  153. zip(xfrms_raw, sgx_xfrms[attr])])
  154. if attr in sgx_miscs:
  155. miscs_raw = bytes([a | b for a, b in
  156. zip(miscs_raw, sgx_miscs[attr])])
  157. return flags_raw, xfrms_raw, miscs_raw
  158. # Generate Checksums / Measurement
  159. def resolve_uri(uri, check_exist=True):
  160. orig_uri = uri
  161. if uri.startswith('file:'):
  162. target = os.path.normpath(uri[len('file:'):])
  163. else:
  164. target = os.path.normpath(uri)
  165. if check_exist and not os.path.exists(target):
  166. raise Exception(
  167. 'Cannot resolve ' + orig_uri + ' or the file does not exist.')
  168. return target
  169. # Resolve an URI relative to manifest file to its absolute path
  170. def resolve_manifest_uri(manifest_path, uri):
  171. if not uri.startswith('file:'):
  172. raise Exception('URI ' + uri + ' is not a local file')
  173. path = uri[len('file:'):]
  174. if os.path.isabs(path):
  175. return path
  176. return os.path.join(os.path.dirname(manifest_path), path)
  177. def get_checksum(filename):
  178. digest = hashlib.sha256()
  179. with open(filename, 'rb') as file:
  180. digest.update(file.read())
  181. return digest.digest()
  182. def get_trusted_files(manifest, args, check_exist=True, do_checksum=True):
  183. targets = dict()
  184. if 'exec' in args:
  185. targets['exec'] = (args['exec'], resolve_uri(args['exec'],
  186. check_exist))
  187. if 'loader.preload' in manifest:
  188. for i, uri in enumerate(str.split(manifest['loader.preload'], ',')):
  189. targets['preload' + str(i)] = (uri, resolve_uri(uri, check_exist))
  190. for (key, val) in manifest.items():
  191. if not key.startswith('sgx.trusted_files.'):
  192. continue
  193. key = key[len('sgx.trusted_files.'):]
  194. if key in targets:
  195. raise Exception(
  196. 'repeated key in manifest: sgx.trusted_files.' + key)
  197. targets[key] = (val, resolve_uri(val, check_exist))
  198. if do_checksum:
  199. for (key, val) in targets.items():
  200. (uri, target) = val
  201. checksum = get_checksum(target).hex()
  202. targets[key] = (uri, target, checksum)
  203. return targets
  204. def get_trusted_children(manifest, check_exist=True, do_checksum=True):
  205. targets = dict()
  206. for (key, val) in manifest.items():
  207. if not key.startswith('sgx.trusted_children.'):
  208. continue
  209. key = key[len('sgx.trusted_children.'):]
  210. if key in targets:
  211. raise Exception(
  212. 'repeated key in manifest: sgx.trusted_children.' + key)
  213. target = resolve_uri(val, check_exist)
  214. if not target.endswith('.sig'):
  215. target += '.sig'
  216. if do_checksum:
  217. sig = open(target, 'rb').read()[
  218. offs.SGX_ARCH_ENCLAVE_CSS_ENCLAVE_HASH:
  219. offs.SGX_ARCH_ENCLAVE_CSS_ENCLAVE_HASH + offs.SGX_HASH_SIZE].hex()
  220. targets[key] = (val, target, sig)
  221. else:
  222. targets[key] = (val, target)
  223. return targets
  224. # Populate Enclave Memory
  225. PAGEINFO_R = 0x1
  226. PAGEINFO_W = 0x2
  227. PAGEINFO_X = 0x4
  228. PAGEINFO_TCS = 0x100
  229. PAGEINFO_REG = 0x200
  230. def get_loadcmds(filename):
  231. loadcmds = []
  232. proc = subprocess.Popen(['readelf', '-l', '-W', filename],
  233. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  234. while True:
  235. line = proc.stdout.readline()
  236. if not line:
  237. break
  238. line = line.decode()
  239. stripped = line.strip()
  240. if not stripped.startswith('LOAD'):
  241. continue
  242. tokens = stripped.split()
  243. if len(tokens) < 6:
  244. continue
  245. if len(tokens) >= 7 and tokens[7] == "E":
  246. tokens[6] += tokens[7]
  247. prot = 0
  248. for token in tokens[6]:
  249. if token == "R":
  250. prot = prot | 4
  251. if token == "W":
  252. prot = prot | 2
  253. if token == "E":
  254. prot = prot | 1
  255. loadcmds.append((int(tokens[1][2:], 16), # offset
  256. int(tokens[2][2:], 16), # addr
  257. int(tokens[4][2:], 16), # filesize
  258. int(tokens[5][2:], 16), # memsize
  259. prot))
  260. proc.wait()
  261. if proc.returncode != 0:
  262. return None
  263. return loadcmds
  264. class MemoryArea:
  265. # pylint: disable=too-few-public-methods,too-many-instance-attributes
  266. def __init__(self, desc, file=None, content=None, addr=None, size=None,
  267. flags=None, measure=True):
  268. # pylint: disable=too-many-arguments
  269. self.desc = desc
  270. self.file = file
  271. self.content = content
  272. self.addr = addr
  273. self.size = size
  274. self.flags = flags
  275. self.is_binary = False
  276. self.measure = measure
  277. if file:
  278. loadcmds = get_loadcmds(file)
  279. if loadcmds:
  280. mapaddr = 0xffffffffffffffff
  281. mapaddr_end = 0
  282. for (_, addr_, _, memsize, _) in loadcmds:
  283. if rounddown(addr_) < mapaddr:
  284. mapaddr = rounddown(addr_)
  285. if roundup(addr_ + memsize) > mapaddr_end:
  286. mapaddr_end = roundup(addr_ + memsize)
  287. self.is_binary = True
  288. self.size = mapaddr_end - mapaddr
  289. if mapaddr > 0:
  290. self.addr = mapaddr
  291. else:
  292. self.size = os.stat(file).st_size
  293. if self.addr is not None:
  294. self.addr = rounddown(self.addr)
  295. if self.size is not None:
  296. self.size = roundup(self.size)
  297. def get_memory_areas(attr, args):
  298. areas = []
  299. areas.append(
  300. MemoryArea('ssa',
  301. size=attr['thread_num'] * SSAFRAMESIZE * offs.SSAFRAMENUM,
  302. flags=PAGEINFO_R | PAGEINFO_W | PAGEINFO_REG))
  303. areas.append(MemoryArea('tcs', size=attr['thread_num'] * offs.TCS_SIZE,
  304. flags=PAGEINFO_TCS))
  305. areas.append(MemoryArea('tls', size=attr['thread_num'] * offs.PAGESIZE,
  306. flags=PAGEINFO_R | PAGEINFO_W | PAGEINFO_REG))
  307. for _ in range(attr['thread_num']):
  308. areas.append(MemoryArea('stack', size=offs.ENCLAVE_STACK_SIZE,
  309. flags=PAGEINFO_R | PAGEINFO_W | PAGEINFO_REG))
  310. for _ in range(attr['thread_num']):
  311. areas.append(MemoryArea('sig_stack', size=offs.ENCLAVE_SIG_STACK_SIZE,
  312. flags=PAGEINFO_R | PAGEINFO_W | PAGEINFO_REG))
  313. areas.append(MemoryArea('pal', file=args['libpal'], flags=PAGEINFO_REG))
  314. if 'exec' in args:
  315. areas.append(MemoryArea('exec', file=args['exec'],
  316. flags=PAGEINFO_W | PAGEINFO_REG))
  317. return areas
  318. def find_areas(areas, desc):
  319. return [area for area in areas if area.desc == desc]
  320. def find_area(areas, desc, allow_none=False):
  321. matching = find_areas(areas, desc)
  322. if not matching and allow_none:
  323. return None
  324. if len(matching) != 1:
  325. raise KeyError(
  326. "Could not find exactly one MemoryArea '{}'".format(desc))
  327. return matching[0]
  328. def entry_point(elf_path):
  329. env = os.environ
  330. env['LC_ALL'] = 'C'
  331. out = subprocess.check_output(
  332. ['readelf', '-l', '--', elf_path], env=env)
  333. for line in out.splitlines():
  334. line = line.decode()
  335. if line.startswith("Entry point "):
  336. return int(line[12:], 0)
  337. raise ValueError("Could not find entry point of elf file")
  338. def baseaddr():
  339. if ENCLAVE_HEAP_MIN == 0:
  340. return offs.ENCLAVE_HIGH_ADDRESS
  341. return 0
  342. def gen_area_content(attr, areas):
  343. # pylint: disable=too-many-locals
  344. manifest_area = find_area(areas, 'manifest')
  345. exec_area = find_area(areas, 'exec', True)
  346. pal_area = find_area(areas, 'pal')
  347. ssa_area = find_area(areas, 'ssa')
  348. tcs_area = find_area(areas, 'tcs')
  349. tls_area = find_area(areas, 'tls')
  350. stacks = find_areas(areas, 'stack')
  351. sig_stacks = find_areas(areas, 'sig_stack')
  352. tcs_data = bytearray(tcs_area.size)
  353. def set_tcs_field(t, offset, pack_fmt, value):
  354. struct.pack_into(pack_fmt, tcs_data, t * offs.TCS_SIZE + offset, value)
  355. tls_data = bytearray(tls_area.size)
  356. def set_tls_field(t, offset, value):
  357. struct.pack_into('<Q', tls_data, t * offs.PAGESIZE + offset, value)
  358. enclave_heap_max = pal_area.addr - offs.MEMORY_GAP
  359. # Sanity check that we measure everything except the heap which is zeroed
  360. # on enclave startup.
  361. for area in areas:
  362. if (area.addr + area.size <= ENCLAVE_HEAP_MIN or
  363. area.addr >= enclave_heap_max or area is exec_area):
  364. if not area.measure:
  365. raise ValueError("Memory area, which is not the heap, "
  366. "is not measured")
  367. elif area.desc != 'free':
  368. raise ValueError("Unexpected memory area is in heap range")
  369. for t in range(0, attr['thread_num']):
  370. ssa_offset = ssa_area.addr + SSAFRAMESIZE * offs.SSAFRAMENUM * t
  371. ssa = baseaddr() + ssa_offset
  372. set_tcs_field(t, offs.TCS_OSSA, '<Q', ssa_offset)
  373. set_tcs_field(t, offs.TCS_NSSA, '<L', offs.SSAFRAMENUM)
  374. set_tcs_field(t, offs.TCS_OENTRY, '<Q',
  375. pal_area.addr + entry_point(pal_area.file))
  376. set_tcs_field(t, offs.TCS_OGS_BASE, '<Q', tls_area.addr + offs.PAGESIZE * t)
  377. set_tcs_field(t, offs.TCS_OFS_LIMIT, '<L', 0xfff)
  378. set_tcs_field(t, offs.TCS_OGS_LIMIT, '<L', 0xfff)
  379. set_tls_field(t, offs.SGX_COMMON_SELF,
  380. tls_area.addr + offs.PAGESIZE * t + baseaddr())
  381. set_tls_field(t, offs.SGX_ENCLAVE_SIZE, attr['enclave_size'])
  382. set_tls_field(t, offs.SGX_TCS_OFFSET, tcs_area.addr + offs.TCS_SIZE * t)
  383. set_tls_field(t, offs.SGX_INITIAL_STACK_OFFSET,
  384. stacks[t].addr + stacks[t].size)
  385. set_tls_field(t, offs.SGX_SIG_STACK_LOW, baseaddr() + sig_stacks[t].addr)
  386. set_tls_field(t, offs.SGX_SIG_STACK_HIGH,
  387. baseaddr() + sig_stacks[t].addr + sig_stacks[t].size)
  388. set_tls_field(t, offs.SGX_SSA, ssa)
  389. set_tls_field(t, offs.SGX_GPR, ssa + SSAFRAMESIZE - offs.SGX_GPR_SIZE)
  390. set_tls_field(t, offs.SGX_MANIFEST_SIZE,
  391. os.stat(manifest_area.file).st_size)
  392. set_tls_field(t, offs.SGX_HEAP_MIN, baseaddr() + ENCLAVE_HEAP_MIN)
  393. set_tls_field(t, offs.SGX_HEAP_MAX, baseaddr() + enclave_heap_max)
  394. if exec_area is not None:
  395. set_tls_field(t, offs.SGX_EXEC_ADDR, baseaddr() + exec_area.addr)
  396. set_tls_field(t, offs.SGX_EXEC_SIZE, exec_area.size)
  397. tcs_area.content = tcs_data
  398. tls_area.content = tls_data
  399. def populate_memory_areas(attr, areas):
  400. populating = attr['enclave_size']
  401. for area in areas:
  402. if area.addr is not None:
  403. continue
  404. area.addr = populating - area.size
  405. if area.addr < ENCLAVE_HEAP_MIN:
  406. raise Exception("Enclave size is not large enough")
  407. populating = max(area.addr - offs.MEMORY_GAP, 0)
  408. free_areas = []
  409. for area in areas:
  410. if area.addr + area.size + offs.MEMORY_GAP < populating:
  411. addr = area.addr + area.size + offs.MEMORY_GAP
  412. flags = PAGEINFO_R | PAGEINFO_W | PAGEINFO_X | PAGEINFO_REG
  413. free_areas.append(
  414. MemoryArea('free', addr=addr, size=populating - addr,
  415. flags=flags, measure=False))
  416. populating = max(area.addr - offs.MEMORY_GAP, 0)
  417. if populating > ENCLAVE_HEAP_MIN:
  418. flags = PAGEINFO_R | PAGEINFO_W | PAGEINFO_X | PAGEINFO_REG
  419. free_areas.append(
  420. MemoryArea('free', addr=ENCLAVE_HEAP_MIN,
  421. size=populating - ENCLAVE_HEAP_MIN, flags=flags,
  422. measure=False))
  423. gen_area_content(attr, areas)
  424. return areas + free_areas
  425. def generate_measurement(attr, areas):
  426. # pylint: disable=too-many-statements,too-many-branches,too-many-locals
  427. def do_ecreate(digest, size):
  428. data = struct.pack("<8sLQ44s", b"ECREATE", SSAFRAMESIZE // offs.PAGESIZE,
  429. size, b"")
  430. digest.update(data)
  431. def do_eadd(digest, offset, flags):
  432. data = struct.pack("<8sQQ40s", b"EADD", offset, flags, b"")
  433. digest.update(data)
  434. def do_eextend(digest, offset, content):
  435. if len(content) != 256:
  436. raise ValueError("Exactly 256 bytes expected")
  437. data = struct.pack("<8sQ48s", b"EEXTEND", offset, b"")
  438. digest.update(data)
  439. digest.update(content)
  440. def include_page(digest, offset, flags, content, measure):
  441. if len(content) != offs.PAGESIZE:
  442. raise ValueError("Exactly one page expected")
  443. do_eadd(digest, offset, flags)
  444. if measure:
  445. for i in range(0, offs.PAGESIZE, 256):
  446. do_eextend(digest, offset + i, content[i:i + 256])
  447. mrenclave = hashlib.sha256()
  448. do_ecreate(mrenclave, attr['enclave_size'])
  449. def print_area(addr, size, flags, desc, measured):
  450. if flags & PAGEINFO_REG:
  451. type_ = 'REG'
  452. if flags & PAGEINFO_TCS:
  453. type_ = 'TCS'
  454. prot = ['-', '-', '-']
  455. if flags & PAGEINFO_R:
  456. prot[0] = 'R'
  457. if flags & PAGEINFO_W:
  458. prot[1] = 'W'
  459. if flags & PAGEINFO_X:
  460. prot[2] = 'X'
  461. prot = ''.join(prot)
  462. desc = '(' + desc + ')'
  463. if measured:
  464. desc += ' measured'
  465. if size == offs.PAGESIZE:
  466. print(" %016x [%s:%s] %s" % (addr, type_, prot, desc))
  467. else:
  468. print(" %016x-%016lx [%s:%s] %s" %
  469. (addr, addr + size, type_, prot, desc))
  470. def load_file(digest, file, offset, addr, filesize, memsize, desc, flags):
  471. # pylint: disable=too-many-arguments
  472. f_addr = rounddown(offset)
  473. m_addr = rounddown(addr)
  474. m_size = roundup(addr + memsize) - m_addr
  475. print_area(m_addr, m_size, flags, desc, True)
  476. for page in range(m_addr, m_addr + m_size, offs.PAGESIZE):
  477. start = page - m_addr + f_addr
  478. end = start + offs.PAGESIZE
  479. start_zero = b""
  480. if start < offset:
  481. if offset - start >= offs.PAGESIZE:
  482. start_zero = ZERO_PAGE
  483. else:
  484. start_zero = bytes(offset - start)
  485. end_zero = b""
  486. if end > offset + filesize:
  487. if end - offset - filesize >= offs.PAGESIZE:
  488. end_zero = ZERO_PAGE
  489. else:
  490. end_zero = bytes(end - offset - filesize)
  491. start += len(start_zero)
  492. end -= len(end_zero)
  493. if start < end:
  494. file.seek(start)
  495. data = file.read(end - start)
  496. else:
  497. data = b""
  498. if len(start_zero + data + end_zero) != offs.PAGESIZE:
  499. raise Exception("wrong calculation")
  500. include_page(digest, page, flags, start_zero + data + end_zero, True)
  501. for area in areas:
  502. if area.file:
  503. with open(area.file, 'rb') as file:
  504. if area.is_binary:
  505. loadcmds = get_loadcmds(area.file)
  506. if loadcmds:
  507. mapaddr = 0xffffffffffffffff
  508. for (offset, addr, filesize, memsize,
  509. prot) in loadcmds:
  510. if rounddown(addr) < mapaddr:
  511. mapaddr = rounddown(addr)
  512. baseaddr_ = area.addr - mapaddr
  513. for (offset, addr, filesize, memsize, prot) in loadcmds:
  514. flags = area.flags
  515. if prot & 4:
  516. flags = flags | PAGEINFO_R
  517. if prot & 2:
  518. flags = flags | PAGEINFO_W
  519. if prot & 1:
  520. flags = flags | PAGEINFO_X
  521. if flags & PAGEINFO_X:
  522. desc = 'code'
  523. else:
  524. desc = 'data'
  525. load_file(mrenclave, file, offset, baseaddr_ + addr,
  526. filesize, memsize, desc, flags)
  527. else:
  528. load_file(mrenclave, file, 0, area.addr,
  529. os.stat(area.file).st_size, area.size,
  530. area.desc, area.flags)
  531. else:
  532. for addr in range(area.addr, area.addr + area.size, offs.PAGESIZE):
  533. data = ZERO_PAGE
  534. if area.content is not None:
  535. start = addr - area.addr
  536. end = start + offs.PAGESIZE
  537. data = area.content[start:end]
  538. include_page(mrenclave, addr, area.flags, data, area.measure)
  539. print_area(area.addr, area.size, area.flags, area.desc,
  540. area.measure)
  541. return mrenclave.digest()
  542. def generate_sigstruct(attr, args, mrenclave):
  543. '''Generate Sigstruct.
  544. field format: (offset, type, value)
  545. ''' # pylint: disable=too-many-locals
  546. fields = {
  547. 'header': (offs.SGX_ARCH_ENCLAVE_CSS_HEADER,
  548. "<4L", 0x00000006, 0x000000e1, 0x00010000, 0x00000000),
  549. 'module_vendor': (offs.SGX_ARCH_ENCLAVE_CSS_MODULE_VENDOR, "<L", 0x00000000),
  550. 'date': (offs.SGX_ARCH_ENCLAVE_CSS_DATE, "<HBB", attr['year'], attr['month'], attr['day']),
  551. 'header2': (offs.SGX_ARCH_ENCLAVE_CSS_HEADER2,
  552. "<4L", 0x00000101, 0x00000060, 0x00000060, 0x00000001),
  553. 'hw_version': (offs.SGX_ARCH_ENCLAVE_CSS_HW_VERSION, "<L", 0x00000000),
  554. 'misc_select': (offs.SGX_ARCH_ENCLAVE_CSS_MISC_SELECT, "4s", attr['misc_select']),
  555. 'misc_mask': (offs.SGX_ARCH_ENCLAVE_CSS_MISC_MASK, "4s", attr['misc_select']),
  556. 'attributes': (offs.SGX_ARCH_ENCLAVE_CSS_ATTRIBUTES, "8s8s", attr['flags'], attr['xfrms']),
  557. 'attribute_mask': (offs.SGX_ARCH_ENCLAVE_CSS_ATTRIBUTE_MASK,
  558. "8s8s", attr['flags'], attr['xfrms']),
  559. 'enclave_hash': (offs.SGX_ARCH_ENCLAVE_CSS_ENCLAVE_HASH, "32s", mrenclave),
  560. 'isv_prod_id': (offs.SGX_ARCH_ENCLAVE_CSS_ISV_PROD_ID, "<H", attr['isv_prod_id']),
  561. 'isv_svn': (offs.SGX_ARCH_ENCLAVE_CSS_ISV_SVN, "<H", attr['isv_svn']),
  562. }
  563. sign_buffer = bytearray(128 + 128)
  564. for field in fields.values():
  565. if field[0] >= offs.SGX_ARCH_ENCLAVE_CSS_MISC_SELECT:
  566. struct.pack_into(field[1], sign_buffer,
  567. field[0] - offs.SGX_ARCH_ENCLAVE_CSS_MISC_SELECT + 128,
  568. *field[2:])
  569. else:
  570. struct.pack_into(field[1], sign_buffer, field[0], *field[2:])
  571. proc = subprocess.Popen(
  572. ['openssl', 'rsa', '-modulus', '-in', args['key'], '-noout'],
  573. stdout=subprocess.PIPE)
  574. modulus_out, _ = proc.communicate()
  575. modulus = bytes.fromhex(modulus_out[8:8+offs.SE_KEY_SIZE*2].decode())
  576. modulus = bytes(reversed(modulus))
  577. proc = subprocess.Popen(
  578. ['openssl', 'sha256', '-binary', '-sign', args['key']],
  579. stdin=subprocess.PIPE, stdout=subprocess.PIPE)
  580. signature, _ = proc.communicate(sign_buffer)
  581. signature = signature[::-1]
  582. modulus_int = int.from_bytes(modulus, byteorder='little')
  583. signature_int = int.from_bytes(signature, byteorder='little')
  584. tmp1 = signature_int * signature_int
  585. q1_int = tmp1 // modulus_int
  586. tmp2 = tmp1 % modulus_int
  587. q2_int = tmp2 * signature_int // modulus_int
  588. q1 = q1_int.to_bytes(384, byteorder='little') # pylint: disable=invalid-name
  589. q2 = q2_int.to_bytes(384, byteorder='little') # pylint: disable=invalid-name
  590. fields.update({
  591. 'modulus': (offs.SGX_ARCH_ENCLAVE_CSS_MODULUS, "384s", modulus),
  592. 'exponent': (offs.SGX_ARCH_ENCLAVE_CSS_EXPONENT, "<L", 3),
  593. 'signature': (offs.SGX_ARCH_ENCLAVE_CSS_SIGNATURE, "384s", signature),
  594. 'q1': (offs.SGX_ARCH_ENCLAVE_CSS_Q1, "384s", q1),
  595. 'q2': (offs.SGX_ARCH_ENCLAVE_CSS_Q2, "384s", q2),
  596. })
  597. buffer = bytearray(offs.SGX_ARCH_ENCLAVE_CSS_SIZE)
  598. for field in fields.values():
  599. struct.pack_into(field[1], buffer, field[0], *field[2:])
  600. return buffer
  601. # Main Program
  602. argparser = argparse.ArgumentParser(
  603. epilog='With sign mode(without -depend), libpal and key are also required. '
  604. 'exec and libpal may be given through manifest options '
  605. 'loader.exec and sgx.enclave_pal_file.')
  606. argparser.add_argument('--output', '-output', metavar='OUTPUT',
  607. type=str, required=True,
  608. help='Output .manifest.sgx file '
  609. '(manifest augmented with autogenerated fields)')
  610. argparser.add_argument('--libpal', '-libpal', metavar='LIBPAL',
  611. type=str, required=False,
  612. help='Input libpal file '
  613. '(required as part of the enclave measurement)')
  614. argparser.add_argument('--key', '-key', metavar='KEY',
  615. type=str, required=False,
  616. help='specify signing key(.pem) file')
  617. argparser.add_argument('--manifest', '-manifest', metavar='MANIFEST',
  618. type=str, required=True,
  619. help='Input .manifest file '
  620. '(user-prepared manifest template)')
  621. argparser.add_argument('--exec', '-exec', metavar='EXEC',
  622. type=str, required=False,
  623. help='Input executable file '
  624. '(required as part of the enclave measurement)')
  625. argparser.add_argument('--depend', '-depend',
  626. action='store_true', required=False,
  627. help='Generate dependency for Makefile')
  628. def parse_args(args):
  629. args = argparser.parse_args(args)
  630. args_dict = {
  631. 'output': args.output,
  632. 'libpal': args.libpal,
  633. 'key': args.key,
  634. 'manifest': args.manifest,
  635. }
  636. if args.exec is not None:
  637. args_dict['exec'] = args.exec
  638. if args.depend:
  639. args_dict['depend'] = True
  640. else:
  641. # key is required and not found in manifest
  642. if args.key is None:
  643. argparser.error("a key is required to sign")
  644. return None
  645. return args_dict
  646. def main_sign(args):
  647. # pylint: disable=too-many-statements,too-many-branches,too-many-locals
  648. manifest, manifest_layout = read_manifest(args['manifest'])
  649. if exec_sig_manifest(args, manifest) != 0:
  650. return 1
  651. # Get attributes from manifest
  652. attr = dict()
  653. parse_int = functools.partial(int, base=0)
  654. for key, default, parse, attr_key in [
  655. ('enclave_size', DEFAULT_ENCLAVE_SIZE, parse_size, 'enclave_size'),
  656. ('thread_num', str(DEFAULT_THREAD_NUM), parse_int, 'thread_num'),
  657. ('isvprodid', '0', parse_int, 'isv_prod_id'),
  658. ('isvsvn', '0', parse_int, 'isv_svn'),
  659. ]:
  660. attr[attr_key] = parse(manifest.setdefault('sgx.' + key, default))
  661. (attr['flags'], attr['xfrms'], attr['misc_select']) = get_enclave_attributes(manifest)
  662. today = datetime.date.today()
  663. attr['year'] = today.year
  664. attr['month'] = today.month
  665. attr['day'] = today.day
  666. print("Attributes:")
  667. print(" size: %d" % attr['enclave_size'])
  668. print(" thread_num: %d" % attr['thread_num'])
  669. print(" isv_prod_id: %d" % attr['isv_prod_id'])
  670. print(" isv_svn: %d" % attr['isv_svn'])
  671. print(" attr.flags: %016x" % int.from_bytes(attr['flags'], byteorder='big'))
  672. print(" attr.xfrm: %016x" % int.from_bytes(attr['xfrms'], byteorder='big'))
  673. print(" misc_select: %08x" % int.from_bytes(attr['misc_select'], byteorder='big'))
  674. print(" date: %d-%02d-%02d" % (attr['year'], attr['month'], attr['day']))
  675. # Check client info for remote attestation
  676. # (if sgx.ra_client.spid is provided)
  677. print("Attestation:")
  678. if 'sgx.ra_client_spid' in manifest and manifest['sgx.ra_client_spid']:
  679. print(" spid: " + manifest['sgx.ra_client_spid'])
  680. if 'sgx.ra_client_key' in manifest and manifest['sgx.ra_client_key']:
  681. print(" key: " + manifest['sgx.ra_client_key'])
  682. else:
  683. print(" *** sgx.ra_client_key not specified ***")
  684. return 1
  685. if 'sgx.ra_client_linkable' in manifest:
  686. print(" linkable: " + manifest['sgx.ra_client_linkable'])
  687. else:
  688. print(" linkable: 0")
  689. else:
  690. print(" *** Client info is not specified. Graphene will not perform"
  691. " remote attestation before execution. Please provide"
  692. " sgx.ra_client_spid and sgx.ra_client_key in the manifest. ***")
  693. # Get trusted checksums and measurements
  694. print("Trusted files:")
  695. for key, val in get_trusted_files(manifest, args).items():
  696. (uri, _, checksum) = val
  697. print(" %s %s" % (checksum, uri))
  698. manifest['sgx.trusted_checksum.' + key] = checksum
  699. print("Trusted children:")
  700. for key, val in get_trusted_children(manifest).items():
  701. (uri, _, mrenclave) = val
  702. print(" %s %s" % (mrenclave, uri))
  703. manifest['sgx.trusted_mrenclave.' + key] = mrenclave
  704. # Try populate memory areas
  705. memory_areas = get_memory_areas(attr, args)
  706. if manifest.get('sgx.static_address', None) is None:
  707. # If static_address is not specified explicitly, deduce from executable
  708. if any([a.addr is not None for a in memory_areas]):
  709. manifest['sgx.static_address'] = '1'
  710. else:
  711. global ENCLAVE_HEAP_MIN # pylint: disable=global-statement
  712. ENCLAVE_HEAP_MIN = 0
  713. manifest['sgx.static_address'] = '0'
  714. if manifest.get('sgx.allow_file_creation', None) is None:
  715. manifest['sgx.allow_file_creation'] = '0'
  716. output_manifest(args['output'], manifest, manifest_layout)
  717. memory_areas = [
  718. MemoryArea('manifest', file=args['output'],
  719. flags=PAGEINFO_R | PAGEINFO_REG)
  720. ] + memory_areas
  721. memory_areas = populate_memory_areas(attr, memory_areas)
  722. print("Memory:")
  723. # Generate measurement
  724. mrenclave = generate_measurement(attr, memory_areas)
  725. print("Measurement:")
  726. print(" %s" % mrenclave.hex())
  727. # Generate sigstruct
  728. open(args['sigfile'], 'wb').write(
  729. generate_sigstruct(attr, args, mrenclave))
  730. return 0
  731. def make_depend(args):
  732. manifest_file = args['manifest']
  733. output = args['output']
  734. (manifest, _) = read_manifest(manifest_file)
  735. if exec_sig_manifest(args, manifest) != 0:
  736. return 1
  737. dependencies = set()
  738. for filename in get_trusted_files(manifest, args, check_exist=False,
  739. do_checksum=False).values():
  740. dependencies.add(filename[1])
  741. for filename in get_trusted_children(manifest, check_exist=False,
  742. do_checksum=False).values():
  743. dependencies.add(filename[1])
  744. dependencies.add(args['libpal'])
  745. dependencies.add(args['key'])
  746. with open(output, 'w') as file:
  747. manifest_sgx = output
  748. if manifest_sgx.endswith('.d'):
  749. manifest_sgx = manifest_sgx[:-len('.d')]
  750. file.write('%s %s:' % (manifest_sgx, args['sigfile']))
  751. for filename in dependencies:
  752. file.write(' \\\n\t%s' % filename)
  753. file.write('\n')
  754. return 0
  755. def main(args=None):
  756. args = parse_args(args)
  757. if args is None:
  758. return 1
  759. if args.get('depend'):
  760. return make_depend(args)
  761. return main_sign(args)