pal-sgx-sign 29 KB

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