pal-sgx-sign 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  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. }
  123. if ARCHITECTURE == 'amd64':
  124. default_attributes.add('FLAG_MODE64BIT')
  125. manifest_options = {
  126. 'debug' : 'FLAG_DEBUG',
  127. 'require_avx' : 'XFRM_AVX',
  128. 'require_avx512' : 'XFRM_AVX512',
  129. 'enable_mpx' : 'XFRM_MPX',
  130. 'support_exinfo' : 'MISC_EXINFO',
  131. }
  132. attributes = default_attributes
  133. for opt in manifest_options.keys():
  134. key = 'sgx.' + opt
  135. if key in manifest:
  136. if manifest[key] == '1':
  137. attributes.add(manifest_options[opt])
  138. else:
  139. attributes.discard(manifest_options[opt])
  140. flags_raw = struct.pack("<Q", 0)
  141. xfrms_raw = struct.pack("<Q", 0)
  142. miscs_raw = struct.pack("<L", 0)
  143. for attr in attributes:
  144. if attr in sgx_flags:
  145. flags_raw = ''.join([chr(ord(a)|ord(b)) for a, b in zip(flags_raw, sgx_flags[attr])])
  146. if attr in sgx_xfrms:
  147. xfrms_raw = ''.join([chr(ord(a)|ord(b)) for a, b in zip(xfrms_raw, sgx_xfrms[attr])])
  148. if attr in sgx_miscs:
  149. miscs_raw = ''.join([chr(ord(a)|ord(b)) for a, b in zip(miscs_raw, sgx_miscs[attr])])
  150. return flags_raw, xfrms_raw, miscs_raw
  151. """ Generate Checksums / Measurement """
  152. def resolve_uri(uri, check_exist=True):
  153. orig_uri = uri
  154. if uri.startswith('file:'):
  155. target = os.path.normpath(uri[5:])
  156. else:
  157. target = os.path.normpath(uri)
  158. if check_exist and not os.path.exists(target):
  159. raise Exception('Cannot resolve ' + orig_uri + ' or the file does not exist.')
  160. return target
  161. def get_checksum(file):
  162. digest = hashlib.sha256()
  163. with open(file, 'rb') as f:
  164. digest.update(f.read())
  165. return digest.digest()
  166. def get_trusted_files(manifest, args):
  167. targets = dict()
  168. if 'exec' in args:
  169. targets['exec'] = (args['exec'], resolve_uri(args['exec']))
  170. if 'loader.preload' in manifest:
  171. i = 0
  172. preloads = []
  173. for uri in str.split(manifest['loader.preload'], ','):
  174. targets['preload' + str(i)] = (uri, resolve_uri(uri))
  175. preloads.append(uri)
  176. i += 1
  177. for (key, val) in manifest.items():
  178. if not key.startswith('sgx.trusted_files.'):
  179. continue
  180. key = key[len('sgx.trusted_files.'):]
  181. if key in targets:
  182. raise Exception('repeated key in manifest: sgx.trusted_files.' + key)
  183. targets[key] = (val, resolve_uri(val))
  184. for (key, val) in targets.items():
  185. (uri, target) = val
  186. checksum = get_checksum(target).encode('hex')
  187. targets[key] = (uri, target, checksum)
  188. return targets
  189. def get_trusted_children(manifest, args):
  190. targets = dict()
  191. for (key, val) in manifest.items():
  192. if not key.startswith('sgx.trusted_children.'):
  193. continue
  194. key = key[len('sgx.trusted_children.'):]
  195. if key in targets:
  196. raise Exception('repeated key in manifest: sgx.trusted_children.' + key)
  197. target = resolve_uri(val)
  198. if not target.endswith('.sig'):
  199. target += '.sig'
  200. sig = open(target, 'rb').read()[960:992].encode('hex')
  201. targets[key] = (val, target, sig)
  202. return targets
  203. """ Populate Enclave Memory """
  204. PAGEINFO_R = 0x1
  205. PAGEINFO_W = 0x2
  206. PAGEINFO_X = 0x4
  207. PAGEINFO_TCS = 0x100
  208. PAGEINFO_REG = 0x200
  209. def get_loadcmds(filename):
  210. loadcmds = []
  211. p = subprocess.Popen(['readelf', '-l', '-W', filename],
  212. stdout=subprocess.PIPE,
  213. stderr=subprocess.PIPE)
  214. while True:
  215. line = p.stdout.readline()
  216. if line == '':
  217. break
  218. stripped = line.strip()
  219. if not stripped.startswith('LOAD'):
  220. continue
  221. tokens = stripped.split()
  222. if len(tokens) < 6:
  223. continue
  224. if len(tokens) >= 7 and tokens[7] == "E":
  225. tokens[6] += tokens[7]
  226. prot = 0
  227. for t in tokens[6]:
  228. if t == "R":
  229. prot = prot | 4
  230. if t == "W":
  231. prot = prot | 2
  232. if t == "E":
  233. prot = prot | 1
  234. loadcmds.append((int(tokens[1][2:], 16), # offset
  235. int(tokens[2][2:], 16), # addr
  236. int(tokens[4][2:], 16), # filesize
  237. int(tokens[5][2:], 16), # memsize
  238. prot))
  239. p.wait()
  240. if p.returncode != 0:
  241. return None
  242. return loadcmds
  243. class MemoryArea:
  244. def __init__(self, desc, file=None, content=None, addr=None, size=None, flags=None, measure=True):
  245. self.desc = desc
  246. self.file = file
  247. self.content = content
  248. self.addr = addr
  249. self.size = size
  250. self.flags = flags
  251. self.is_binary = False
  252. self.measure = measure
  253. if file:
  254. loadcmds = get_loadcmds(file)
  255. if loadcmds:
  256. mapaddr = 0xffffffffffffffff
  257. mapaddr_end = 0
  258. for (offset, addr, filesize, memsize, prot) in loadcmds:
  259. if rounddown(addr) < mapaddr:
  260. mapaddr = rounddown(addr)
  261. if roundup(addr + memsize) > mapaddr_end:
  262. mapaddr_end = roundup(addr + memsize)
  263. self.is_binary = True
  264. self.size = mapaddr_end - mapaddr
  265. if mapaddr > 0:
  266. self.addr = mapaddr
  267. else:
  268. self.size = os.stat(file).st_size
  269. if self.addr is not None:
  270. self.addr = rounddown(self.addr)
  271. if self.size is not None:
  272. self.size = roundup(self.size)
  273. def get_memory_areas(manifest, attr, args):
  274. areas = []
  275. areas.append(MemoryArea('ssa', size=attr['thread_num'] * SSAFRAMESIZE * SSAFRAMENUM,
  276. flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_REG))
  277. areas.append(MemoryArea('tcs', size=attr['thread_num'] * TCS_SIZE,
  278. flags=PAGEINFO_TCS))
  279. areas.append(MemoryArea('tls', size=attr['thread_num'] * PAGESIZE,
  280. flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_REG))
  281. for t in range(attr['thread_num']):
  282. areas.append(MemoryArea('stack', size=ENCLAVE_STACK_SIZE,
  283. flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_REG))
  284. areas.append(MemoryArea('pal', file=args['libpal'], flags=PAGEINFO_REG))
  285. if 'exec' in args:
  286. areas.append(MemoryArea('exec', file=args['exec'],
  287. flags=PAGEINFO_W|PAGEINFO_REG))
  288. return areas
  289. def find_areas(areas, desc):
  290. return filter(lambda area: area.desc == desc, areas)
  291. def find_area(areas, desc, allow_none=False):
  292. matching = find_areas(areas, desc)
  293. if len(matching) == 0 and allow_none:
  294. return None
  295. if len(matching) != 1:
  296. raise KeyError("Could not find exactly one MemoryArea '{}'".format(desc))
  297. return matching[0]
  298. def entry_point(elf_path):
  299. env = os.environ
  300. env['LC_ALL'] = 'C'
  301. out = subprocess.check_output(['readelf', '-l', '--', elf_path], env = env)
  302. for line in out.splitlines():
  303. if line.startswith("Entry point "):
  304. return parse_int(line[12:])
  305. raise ValueError("Could not find entry point of elf file")
  306. def baseaddr():
  307. if enclave_heap_min == 0:
  308. return ENCLAVE_HIGH_ADDRESS
  309. else:
  310. return 0
  311. def gen_area_content(attr, areas):
  312. manifest_area = find_area(areas, 'manifest')
  313. exec_area = find_area(areas, 'exec', True)
  314. pal_area = find_area(areas, 'pal')
  315. ssa_area = find_area(areas, 'ssa')
  316. tcs_area = find_area(areas, 'tcs')
  317. tls_area = find_area(areas, 'tls')
  318. stacks = find_areas(areas, 'stack')
  319. tcs_data = bytearray(tcs_area.size)
  320. def set_tcs_field(t, offset, pack_fmt, value):
  321. struct.pack_into(pack_fmt, tcs_data, t * TCS_SIZE + offset, value)
  322. tls_data = bytearray(tls_area.size)
  323. def set_tls_field(t, offset, value):
  324. struct.pack_into('<Q', tls_data, t * PAGESIZE + offset, value)
  325. enclave_heap_max = pal_area.addr - MEMORY_GAP
  326. # Sanity check that we measure everything except the heap which is zeroed
  327. # on enclave startup.
  328. for area in areas:
  329. if area.addr + area.size <= enclave_heap_min or area.addr >= enclave_heap_max or area is exec_area:
  330. if not area.measure:
  331. raise ValueError("Memory area, which is not the heap, is not measured")
  332. elif area.desc != 'free':
  333. raise ValueError("Unexpected memory area is in heap range")
  334. for t in range(0, attr['thread_num']):
  335. ssa_offset = ssa_area.addr + SSAFRAMESIZE * SSAFRAMENUM * t;
  336. ssa = baseaddr() + ssa_offset
  337. set_tcs_field(t, TCS_OSSA, '<Q', ssa_offset)
  338. set_tcs_field(t, TCS_NSSA, '<L', SSAFRAMENUM)
  339. set_tcs_field(t, TCS_OENTRY, '<Q', pal_area.addr + entry_point(pal_area.file))
  340. set_tcs_field(t, TCS_OGSBASGX, '<Q', tls_area.addr + PAGESIZE * t)
  341. set_tcs_field(t, TCS_FSLIMIT, '<L', 0xfff)
  342. set_tcs_field(t, TCS_GSLIMIT, '<L', 0xfff)
  343. set_tls_field(t, SGX_COMMON_SELF, tls_area.addr + PAGESIZE * t + baseaddr())
  344. set_tls_field(t, SGX_ENCLAVE_SIZE, attr['enclave_size'])
  345. set_tls_field(t, SGX_TCS_OFFSET, tcs_area.addr + TCS_SIZE * t)
  346. set_tls_field(t, SGX_INITIAL_STACK_OFFSET, stacks[t].addr + stacks[t].size)
  347. set_tls_field(t, SGX_SSA, ssa)
  348. set_tls_field(t, SGX_GPR, ssa + SSAFRAMESIZE - SGX_GPR_SIZE)
  349. set_tls_field(t, SGX_MANIFEST_SIZE, os.stat(manifest_area.file).st_size)
  350. set_tls_field(t, SGX_HEAP_MIN, baseaddr() + enclave_heap_min)
  351. set_tls_field(t, SGX_HEAP_MAX, baseaddr() + enclave_heap_max)
  352. if exec_area is not None:
  353. set_tls_field(t, SGX_EXEC_ADDR, baseaddr() + exec_area.addr)
  354. set_tls_field(t, SGX_EXEC_SIZE, exec_area.size)
  355. tcs_area.content = tcs_data
  356. tls_area.content = tls_data
  357. def populate_memory_areas(manifest, attr, areas):
  358. populating = attr['enclave_size']
  359. for area in areas:
  360. if area.addr is not None:
  361. continue
  362. area.addr = populating - area.size
  363. if area.addr < enclave_heap_min:
  364. raise Exception("Enclave size is not large enough")
  365. if area.desc == 'exec':
  366. populating = area.addr;
  367. else:
  368. populating = area.addr - MEMORY_GAP
  369. free_areas = []
  370. for area in areas:
  371. if area.addr + area.size < populating:
  372. addr = area.addr + area.size
  373. free_areas.append(MemoryArea('free', addr=addr, size=populating - addr,
  374. flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_X|PAGEINFO_REG,
  375. measure=False))
  376. populating = area.addr
  377. if populating > enclave_heap_min:
  378. free_areas.append(MemoryArea('free', addr=enclave_heap_min,
  379. size=populating - enclave_heap_min,
  380. flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_X|PAGEINFO_REG,
  381. measure=False))
  382. gen_area_content(attr, areas)
  383. return areas + free_areas
  384. def generate_measurement(attr, areas):
  385. def do_ecreate(digest, size):
  386. data = struct.pack("<8sLQ44s", "ECREATE", SSAFRAMESIZE / PAGESIZE, size, "")
  387. digest.update(data)
  388. def do_eadd(digest, offset, flags):
  389. data = struct.pack("<8sQQ40s", "EADD", offset, flags, "")
  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", "EEXTEND", offset, "")
  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 >>sys.stderr, " %016x [%s:%s] %s" % (addr, type, prot, desc)
  424. else:
  425. print >>sys.stderr, " %016x-%016lx [%s:%s] %s" % (addr, addr + size, type, prot, desc)
  426. def load_file(digest, f, offset, addr, filesize, memsize, desc, flags):
  427. f_addr = rounddown(offset)
  428. m_addr = rounddown(addr)
  429. f_size = roundup(offset + filesize) - f_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 = ""
  436. if start < offset:
  437. if offset - start >= PAGESIZE:
  438. start_zero = ZERO_PAGE
  439. else:
  440. start_zero = chr(0) * (offset - start)
  441. end_zero = ""
  442. if end > offset + filesize:
  443. if end - offset - filesize >= PAGESIZE:
  444. end_zero = ZERO_PAGE
  445. else:
  446. end_zero = chr(0) * (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 = ""
  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, prot) in loadcmds:
  465. if rounddown(addr) < mapaddr:
  466. mapaddr = rounddown(addr)
  467. baseaddr = area.addr - mapaddr
  468. for (offset, addr, filesize, memsize, prot) in loadcmds:
  469. flags = area.flags
  470. if prot & 4:
  471. flags = flags | PAGEINFO_R
  472. if prot & 2:
  473. flags = flags | PAGEINFO_W
  474. if prot & 1:
  475. flags = flags | PAGEINFO_X
  476. if flags & PAGEINFO_X:
  477. desc = 'code'
  478. else:
  479. desc = 'data'
  480. load_file(mrenclave, f, offset, baseaddr + addr,
  481. filesize, memsize, desc, flags)
  482. else:
  483. load_file(mrenclave, f, 0, area.addr,
  484. os.stat(area.file).st_size, area.size,
  485. area.desc, area.flags)
  486. else:
  487. for a in range(area.addr, area.addr + area.size, PAGESIZE):
  488. data = ZERO_PAGE
  489. if area.content is not None:
  490. start = a - area.addr
  491. end = start + PAGESIZE
  492. data = area.content[start:end]
  493. include_page(mrenclave, a, area.flags, data, area.measure)
  494. print_area(area.addr, area.size, area.flags, area.desc, area.measure)
  495. return mrenclave.digest()
  496. """ Generate Sigstruct """
  497. def generate_sigstruct(attr, args, mrenclave):
  498. today = datetime.date.today()
  499. # field format: (offset, type, value)
  500. fields = dict()
  501. fields['header'] = (SGX_ARCH_SIGSTRUCT_HEADER,
  502. "<4L", 0x00000006, 0x000000e1, 0x00010000, 0x00000000)
  503. fields['vendor'] = (SGX_ARCH_SIGSTRUCT_VENDOR,
  504. "<L", 0x00000000)
  505. fields['date'] = (SGX_ARCH_SIGSTRUCT_DATE,
  506. "<HBB", today.year, today.month, today.day)
  507. fields['header2'] = (SGX_ARCH_SIGSTRUCT_HEADER2,
  508. "<4L", 0x00000101, 0x00000060, 0x00000060, 0x00000001)
  509. fields['swdefined'] = (SGX_ARCH_SIGSTRUCT_SWDEFINED,
  510. "<L", 0x00000000)
  511. fields['miscs'] = (SGX_ARCH_SIGSTRUCT_MISCSELECT,
  512. "4s", attr['miscs'])
  513. fields['miscmask'] = (SGX_ARCH_SIGSTRUCT_MISCSELECT_MASK,
  514. "4s", attr['miscs'])
  515. fields['attrs'] = (SGX_ARCH_SIGSTRUCT_ATTRIBUTES,
  516. "8s8s", attr['flags'], attr['xfrms'])
  517. fields['attrmask'] = (SGX_ARCH_SIGSTRUCT_ATTRIBUTES_MASK,
  518. "8s8s", attr['flags'], attr['xfrms'])
  519. fields['mrenclave'] = (SGX_ARCH_SIGSTRUCT_ENCLAVE_HASH,
  520. "32s", mrenclave)
  521. fields['isvprodid'] = (SGX_ARCH_SIGSTRUCT_ISVPRODID,
  522. "<H", attr['isvprodid'])
  523. fields['isvsvn'] = (SGX_ARCH_SIGSTRUCT_ISVSVN,
  524. "<H", attr['isvsvn'])
  525. sign_buffer = bytearray(128 + 128)
  526. for key, field in fields.items():
  527. if field[0] >= 900:
  528. struct.pack_into(field[1], sign_buffer, field[0] - 900 + 128, *field[2:])
  529. else:
  530. struct.pack_into(field[1], sign_buffer, field[0], *field[2:])
  531. p = subprocess.Popen(['openssl', 'rsa', '-modulus', '-in', args['key'], '-noout'], stdout=subprocess.PIPE)
  532. modulus_out = p.communicate()[0]
  533. modulus = modulus_out[8:8+384*2].lower().decode('hex')
  534. modulus = modulus[::-1]
  535. p = subprocess.Popen(['openssl', 'sha256', '-binary', '-sign', args['key']],
  536. stdin=subprocess.PIPE, stdout=subprocess.PIPE)
  537. signature = p.communicate(sign_buffer)[0]
  538. signature = signature[::-1]
  539. def bytes_to_int(bytes):
  540. i = 0
  541. q = 1
  542. for digit in bytes:
  543. if ord(digit) != 0:
  544. i = i + ord(digit) * q
  545. q = q * 256
  546. return i
  547. def int_to_bytes(i):
  548. b = ""
  549. l = 0
  550. while i > 0:
  551. b = b + chr(i % 256)
  552. i = i // 256
  553. l = l + 1
  554. return b
  555. modulus_int = bytes_to_int(modulus)
  556. signature_int = bytes_to_int(signature)
  557. tmp1 = signature_int * signature_int
  558. q1_int = tmp1 // modulus_int
  559. tmp2 = tmp1 % modulus_int
  560. q2_int = tmp2 * signature_int // modulus_int
  561. q1 = int_to_bytes(q1_int)
  562. q2 = int_to_bytes(q2_int)
  563. fields['modulus'] = (SGX_ARCH_SIGSTRUCT_MODULUS, "384s", modulus)
  564. fields['exponent'] = (SGX_ARCH_SIGSTRUCT_EXPONENT, "<L", 3)
  565. fields['signature'] = (SGX_ARCH_SIGSTRUCT_SIGNATURE, "384s", signature)
  566. fields['q1'] = (SGX_ARCH_SIGSTRUCT_Q1, "384s", q1)
  567. fields['q2'] = (SGX_ARCH_SIGSTRUCT_Q2, "384s", q2)
  568. buffer = bytearray(SGX_ARCH_SIGSTRUCT_SIZE)
  569. for key, field in fields.items():
  570. struct.pack_into(field[1], buffer, field[0], *field[2:])
  571. return buffer
  572. """ Main Program """
  573. options = {
  574. # Option name : (Required Value)
  575. 'output': (True, 'output'),
  576. 'libpal': (True, 'libpal path'),
  577. 'key': (True, 'signing key'),
  578. 'manifest': (True, 'manifest'),
  579. 'exec': (False, 'executable'),
  580. }
  581. def usage():
  582. usage_message = 'USAGE: ' + sys.argv[0] + ' -help|-h'
  583. for opt, optval in options.items():
  584. if not optval[0]:
  585. usage_message += '['
  586. usage_message += '|-' + opt
  587. if optval[1]:
  588. usage_message += ' <' + optval[1] + '>'
  589. if not optval[0]:
  590. usage_message += ']'
  591. print >> sys.stderr, usage_message
  592. os._exit(-1)
  593. def parse_args():
  594. args = dict()
  595. for opt, optval in options.items():
  596. if not optval[1]:
  597. args[opt] = False
  598. i = 1
  599. while i < len(sys.argv):
  600. got = sys.argv[i]
  601. if got == '-help' or got == '-h':
  602. usage()
  603. invalid = True
  604. for opt, optval in options.items():
  605. if got != '-' + opt:
  606. continue
  607. if optval[1] is not None:
  608. i += 1
  609. if i == len(sys.argv):
  610. print >>sys.stderr, "Option %s needs a value." % (opt)
  611. usage()
  612. args[opt] = sys.argv[i]
  613. else:
  614. args[opt] = True
  615. invalid = False
  616. break
  617. if invalid:
  618. print >>sys.stderr, "Unknown option: %s." % (got[1:])
  619. usage()
  620. i += 1
  621. for opt, optval in options.items():
  622. if optval[0] and opt not in args:
  623. print >>sys.stderr, "Must specify %s <%s>." % (opt, optval[1])
  624. usage()
  625. return args
  626. if __name__ == "__main__":
  627. # Parse arguments
  628. args = parse_args()
  629. (manifest, manifest_layout) = read_manifest(args['manifest'])
  630. if 'exec' not in args:
  631. if 'loader.exec' in manifest:
  632. exec_url = manifest['loader.exec']
  633. if exec_url[:5] != 'file:':
  634. print "executable must be a local file"
  635. os._exit(-1)
  636. args['exec'] = os.path.join(os.path.dirname(args['manifest']), exec_url[5:])
  637. args['root'] = os.path.dirname(os.path.abspath(args['output']))
  638. if 'sgx.sigfile' in manifest:
  639. args['sigfile'] = resolve_uri(manifest['sgx.sigfile'], False)
  640. else:
  641. sigfile = args['output']
  642. for ext in ['.manifest.sgx', '.manifest']:
  643. if sigfile.endswith(ext):
  644. sigfile = sigfile[:-len(ext)]
  645. break
  646. args['sigfile'] = sigfile + '.sig'
  647. manifest['sgx.sigfile'] = 'file:' + os.path.basename(args['sigfile'])
  648. # Get attributes from manifest
  649. attr = dict()
  650. for key, default, parse in [
  651. ('enclave_size', DEFAULT_ENCLAVE_SIZE, parse_size),
  652. ('thread_num', str(DEFAULT_THREAD_NUM), parse_int),
  653. ('isvprodid', '0', parse_int),
  654. ('isvsvn', '0', parse_int),
  655. ]:
  656. if 'sgx.' + key not in manifest:
  657. manifest['sgx.' + key] = default
  658. attr[key] = parse(manifest['sgx.' + key])
  659. (attr['flags'], attr['xfrms'], attr['miscs']) = get_enclave_attributes(manifest)
  660. print >>sys.stderr, "Attributes:"
  661. print >>sys.stderr, " size: %d" % (attr['enclave_size'])
  662. print >>sys.stderr, " threadnum: %d" % (attr['thread_num'])
  663. print >>sys.stderr, " isvprodid: %d" % (attr['isvprodid'])
  664. print >>sys.stderr, " isvsvn: %d" % (attr['isvsvn'])
  665. print >>sys.stderr, " flags: %016x" % (bytes_to_int(attr['flags']))
  666. print >>sys.stderr, " xfrms: %016x" % (bytes_to_int(attr['xfrms']))
  667. print >>sys.stderr, " miscs: %08x" % (bytes_to_int(attr['miscs']))
  668. # Check client info for remote attestation. Skip and warn if sgx.ra_client.spid is not provided.
  669. print >>sys.stderr, "Attestation:"
  670. if 'sgx.ra_client_spid' in manifest and manifest['sgx.ra_client_spid']:
  671. print >>sys.stderr, " spid: " + manifest['sgx.ra_client_spid']
  672. need_client_info = False
  673. if 'sgx.ra_client_key' not in manifest or not manifest['sgx.ra_client_key']:
  674. print >>sys.stderr, " *** sgx.ra_client_key not specified ***"
  675. need_client_info = True
  676. else:
  677. print >>sys.stderr, " key: " + manifest['sgx.ra_client_key']
  678. if 'sgx.ra_client_linkable' in manifest:
  679. print >>sys.stderr, " linkable: " + manifest['sgx.ra_client_linkable']
  680. else:
  681. print >>sys.stderr, " linkable: 0"
  682. if need_client_info: sys.exit(-1)
  683. else:
  684. print >>sys.stderr, " *** Client info is not specified. Graphene" + \
  685. " will not perform remote attestation before execution." + \
  686. " Please provide sgx.ra_client_spid and sgx.ra_client_key in the manifest. ***"
  687. # Get trusted checksums and measurements
  688. print >>sys.stderr, "Trusted files:"
  689. for key, val in get_trusted_files(manifest, args).items():
  690. (uri, target, checksum) = val
  691. print >>sys.stderr, " %s %s" % (checksum, uri)
  692. manifest['sgx.trusted_checksum.' + key] = checksum
  693. print >>sys.stderr, "Trusted children:"
  694. for key, val in get_trusted_children(manifest, args).items():
  695. (uri, target, mrenclave) = val
  696. print >>sys.stderr, " %s %s" % (mrenclave, uri)
  697. manifest['sgx.trusted_mrenclave.' + key] = mrenclave
  698. # Try populate memory areas
  699. memory_areas = get_memory_areas(manifest, attr, args)
  700. if len([a for a in memory_areas if a.addr is not None]) > 0:
  701. manifest['sgx.static_address'] = '1'
  702. else:
  703. enclave_heap_min = 0
  704. # Add manifest at the top
  705. shutil.copy2(args['manifest'], args['output'])
  706. output_manifest(args['output'], manifest, manifest_layout)
  707. memory_areas = [
  708. MemoryArea('manifest', file=args['output'],
  709. flags=PAGEINFO_R|PAGEINFO_REG)
  710. ] + memory_areas
  711. memory_areas = populate_memory_areas(manifest, attr, memory_areas)
  712. print >>sys.stderr, "Memory:"
  713. # Generate measurement
  714. mrenclave = generate_measurement(attr, memory_areas)
  715. print >>sys.stderr, "Measurement:"
  716. print >>sys.stderr, " " + mrenclave.encode('hex')
  717. # Generate sigstruct
  718. open(args['sigfile'], 'wb').write(generate_sigstruct(attr, args, mrenclave))