Bug 1091668 - Import eme voucher script; r=ted, r=gerv
authorMike Shal <mshal@mozilla.com>
Mon, 03 Nov 2014 11:24:55 -0500
changeset 241622 2814563e4abf0f780db718e49775d603e0c9b56e
parent 241621 272903931997f392049113fffa767bb157c1b592
child 241623 25800dd796617b9b1f46b8c315df30347f98d77a
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, gerv
bugs1091668
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1091668 - Import eme voucher script; r=ted, r=gerv
python/eme/gen-eme-voucher.py
new file mode 100644
--- /dev/null
+++ b/python/eme/gen-eme-voucher.py
@@ -0,0 +1,264 @@
+#!/usr/bin/env python3.3
+# Copyright 2014 Adobe Systems Incorporated. All Rights Reserved.
+#
+# Adobe permits you to use, modify, and distribute this file in accordance
+# with the terms of the Mozilla Public License, v 2.0 accompanying it.  If
+# a copy of the MPL was not distributed with this file, You can obtain one
+# at http://mozilla.org/MPL/2.0/.
+#
+# Create perforce changelist of modules from FTP server
+
+import io, argparse,pyasn1, bitstring
+from pyasn1.codec.der import encoder, decoder
+from pyasn1.type import univ, namedtype, namedval, constraint
+import hashlib
+
+# CodeSectionDigest ::= SEQUENCE {
+# offset				INTEGER --  section's file offset in the signed binary
+# digestAlgorithm		OBJECT IDENTIFIER -- algorithm identifier for the hash value below. For now only supports SHA256.
+# digestValue			OCTET STRING -- hash value of the TEXT segment.
+# }
+class CodeSectionDigest(univ.Sequence):
+	componentType = namedtype.NamedTypes(
+		namedtype.NamedType('offset', univ.Integer()),
+		namedtype.NamedType('digestAlgorithm', univ.ObjectIdentifier()),
+		namedtype.NamedType('digest', univ.OctetString()))
+
+# CodeSegmentDigest ::= SEQUENCE {
+#	 offset				INTEGER -- TEXT segment's file offset in the signed binary
+#	 codeSectionDigests			SET OF CodeSectionDigests
+# }
+
+class SetOfCodeSectionDigest(univ.SetOf):
+	componentType = CodeSectionDigest()
+
+class CodeSegmentDigest(univ.Sequence):
+	componentType = namedtype.NamedTypes(
+		namedtype.NamedType('offset', univ.Integer()),
+		namedtype.NamedType('codeSectionDigests', SetOfCodeSectionDigest()))
+
+# ArchitectureDigest ::= SEQUENCE {
+# 	cpuType                ENUMERATED CpuType
+# 	cpuSubType				ENUMERATED CpuSubType
+# 	CodeSegmentDigests		SET OF CodeSegmentDigests
+# }
+
+class SetOfCodeSegmentDigest(univ.SetOf):
+	componentType = CodeSegmentDigest()
+
+class CPUType(univ.Enumerated):
+	namedValues = namedval.NamedValues(
+		('IMAGE_FILE_MACHINE_I386', 0x14c),
+		('IMAGE_FILE_MACHINE_AMD64',0x8664 )
+	)
+	subtypeSpec = univ.Enumerated.subtypeSpec + \
+				  constraint.SingleValueConstraint(0x14c, 0x8664)
+
+
+class ArchitectureDigest(univ.Sequence):
+	componentType = namedtype.NamedTypes(
+		namedtype.NamedType('cpuType', CPUType()),
+		namedtype.NamedType('cpuSubType', univ.Integer()),
+		namedtype.NamedType('CodeSegmentDigests', SetOfCodeSegmentDigest())
+	)
+
+#	 ApplicationDigest ::= SEQUENCE {
+#	 version    INTEGER
+#	 digests    SET OF ArchitectureDigest
+#	 }
+class SetOfArchitectureDigest(univ.SetOf):
+	componentType = ArchitectureDigest()
+
+class ApplicationDigest(univ.Sequence):
+	componentType = namedtype.NamedTypes(
+		namedtype.NamedType('version', univ.Integer()),
+		namedtype.NamedType('digests', SetOfArchitectureDigest())
+	)
+
+def meetsRequirements(items, requirements):
+	for r in requirements:
+		for n, v in r.items():
+			if n not in items or items[n] != v: return False
+	return True
+
+# return total number of bytes read from items_in excluding leaves
+def parseItems(stream, items_in, items_out):
+	bits_read = 0
+	total_bits_read = 0
+
+	for item in items_in:
+		name = item[0]
+		t = item[1]
+		bits = 1 if ":" not in t else int(t[t.index(":") + 1:])
+
+		if ":" in t and t.find("bytes") >= 0:
+			bits = bits * 8
+
+		if len(item) == 2:
+			items_out[name] = stream.read(t)
+			bits_read += bits
+			total_bits_read += bits
+		elif len(item) == 3 or len(item) == 4:
+			requirements = list(filter(lambda x: isinstance(x, dict), item[2]))
+			sub_items = list(filter(lambda x: isinstance(x, tuple), item[2]))
+
+			if not meetsRequirements(items_out, requirements): continue
+
+			# has sub-items based on length
+			items_out[name] = stream.read(t)
+			bits_read += bits
+			total_bits_read += bits
+
+			if len(item) == 4:
+				bit_length = items_out[name] * 8
+
+				if bit_length > 0:
+					sub_read, sub_total_read = parseItems(stream, sub_items, items_out)
+					bit_length -= sub_read
+					total_bits_read += sub_total_read
+
+					if bit_length > 0:
+						items_out[item[3]] = stream.read('bits:' + str(bit_length))
+						bits_read += bit_length
+						total_bits_read += bit_length
+		else:
+			raise Exception("unrecognized item" + pprint.pformat(item))
+
+	return bits_read, total_bits_read
+
+
+class SectionHeader:
+	def __init__(self, stream):
+		items = [
+			('Name', 'bytes:8'),
+			('VirtualSize', 'uintle:32'),
+			('VirtualAddress', 'uintle:32'),
+			('SizeOfRawData', 'uintle:32'),
+			('PointerToRawData', 'uintle:32'),
+			('PointerToRelocations', 'uintle:32'),
+			('PointerToLineNumber', 'uintle:32'),
+			('NumberOfRelocations', 'uintle:16'),
+			('NumberOfLineNumbers', 'uintle:16'),
+			('Characteristics', 'uintle:32')
+		]
+		self.items = {}
+		_, self.bits_read = parseItems(stream, items, self.items)
+
+		self.sectionName = self.items['Name'].decode('utf-8')
+		self.offset = self.items['PointerToRawData']
+
+
+class COFFFileHeader:
+	def __init__(self, stream):
+		items = [
+			('Machine', 'uintle:16'),
+			('NumberOfSections', 'uintle:16'),
+			('TimeDateStamp', 'uintle:32'),
+			('PointerToSymbolTable', 'uintle:32'),
+			('NumberOfSymbols', 'uintle:32'),
+			('SizeOfOptionalHeader', 'uintle:16'),
+			('Characteristics', 'uintle:16')
+		]
+		self.items = {}
+		_, self.bits_read = parseItems(stream, items, self.items)
+
+		#skip over optional header.
+		if self.items['SizeOfOptionalHeader'] > 0:
+			stream.read(self.items['SizeOfOptionalHeader'] * 8)
+			self.bits_read += self.items['SizeOfOptionalHeader'] * 8
+
+		#start reading section headers
+		numberOfSections = self.items['NumberOfSections']
+		self.codeSectionHeaders = []
+
+		while numberOfSections > 0 :
+			sectionHeader = SectionHeader(stream)
+			if (sectionHeader.items['Characteristics'] & 0x20000000) == 0x20000000:
+				self.codeSectionHeaders.append(sectionHeader)
+			numberOfSections -= 1
+
+		self.codeSectionHeaders.sort(key=lambda header: header.offset)
+
+
+def main():
+	parser = argparse.ArgumentParser(description='PE/COFF Parser.')
+	parser.add_argument('-input', required=True, help="File to parse.")
+	parser.add_argument('-output', required=True, help="File to write to.")
+	parser.add_argument('-verbose', action='store_true', help="Verbose output.")
+	app_args = parser.parse_args()
+
+	stream = bitstring.ConstBitStream(filename=app_args.input)
+	# find the COFF header.
+	# skip forward past the MSDOS stub header to 0x3c.
+
+	stream.bytepos = 0x3c
+
+	# read 4 bytes, this is the file offset of the PE signature.
+	peSignatureOffset = stream.read('uintle:32')
+	stream.bytepos = peSignatureOffset
+
+	#read 4 bytes, make sure it's a PE signature.
+	signature = stream.read('uintle:32')
+	if signature != 0x00004550 :
+		return
+
+
+	archDigest = ArchitectureDigest()
+
+	codeSegmentDigests = SetOfCodeSegmentDigest()
+	codeSegmentIdx = 0
+
+	#after signature is the actual COFF file header.
+	coffFileHeader = COFFFileHeader(stream)
+
+	if coffFileHeader.items['Machine'] == 0x14c:
+		archDigest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_I386'))
+	elif coffFileHeader.items['Machine'] == 0x8664:
+		archDigest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_AMD64'))
+
+	archDigest.setComponentByName('cpuSubType', 0)
+
+
+	for codeSectionHeader in coffFileHeader.codeSectionHeaders:
+		stream.bytepos = codeSectionHeader.offset
+		codeSectionBytes = stream.read('bytes:'+ str(codeSectionHeader.items['SizeOfRawData']))
+		if codeSectionHeader.items['SizeOfRawData'] < codeSectionHeader.items['VirtualSize']:
+			#zero pad up to virtualSize
+			codeSectionBytes += "\0" * (codeSectionHeader.items['VirtualSize']-codeSectionHeader.items['SizeOfRawData'])
+
+		digester = hashlib.sha256()
+		digester.update(codeSectionBytes)
+		digest = digester.digest()
+
+		codeSectionDigest = CodeSectionDigest()
+		codeSectionDigest.setComponentByName('offset', codeSectionHeader.offset)
+		codeSectionDigest.setComponentByName('digestAlgorithm', univ.ObjectIdentifier('2.16.840.1.101.3.4.2.1'))
+		codeSectionDigest.setComponentByName('digest', univ.OctetString(digest))
+
+		setOfDigest = SetOfCodeSectionDigest()
+		setOfDigest.setComponentByPosition(0, codeSectionDigest)
+
+		codeSegmentDigest = CodeSegmentDigest()
+		codeSegmentDigest.setComponentByName('offset', codeSectionHeader.offset)
+		codeSegmentDigest.setComponentByName('codeSectionDigests', setOfDigest)
+
+		codeSegmentDigests.setComponentByPosition(codeSegmentIdx, codeSegmentDigest)
+		codeSegmentIdx += 1
+
+	archDigest.setComponentByName('CodeSegmentDigests', codeSegmentDigests)
+
+	setOfArchDigests = SetOfArchitectureDigest()
+	setOfArchDigests.setComponentByPosition(0, archDigest)
+
+	appDigest = ApplicationDigest()
+
+	appDigest.setComponentByName('version', 1)
+	appDigest.setComponentByName('digests', setOfArchDigests)
+
+	binaryDigest = encoder.encode(appDigest)
+
+	outFile = open(app_args.output, 'wb')
+	outFile.write(binaryDigest)
+
+if __name__ == '__main__':
+	main()