pal-sgx-sign 28 KB

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