예제 #1
0
파일: File.py 프로젝트: zgc/nut
	def setupCrypto(self, cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
		if cryptoType != -1:
			self.cryptoType = cryptoType
			
		if cryptoKey != -1:
			self.cryptoKey = cryptoKey
			
		if cryptoCounter != -1:
			self.cryptoCounter = cryptoCounter
			
		if self.cryptoType == Fs.Type.Crypto.CTR:
			if self.cryptoKey:
				self.crypto = aes128.AESCTR(self.cryptoKey, self.setCounter(self.offset))
				self.cryptoType = Fs.Type.Crypto.CTR
			
				self.enableBufferedIO(0x10, 0x10)

		elif self.cryptoType == Fs.Type.Crypto.XTS:
			if self.cryptoKey:
				self.crypto = aes128.AESXTS(self.cryptoKey)
				self.cryptoType = Fs.Type.Crypto.XTS
			
				if self.size < 1 or self.size > 0xFFFFFF:
					raise IOError('AESXTS Block too large or small')
			
				self.rewind()
				self.enableBufferedIO(self.size, 0x10)

		elif self.cryptoType == Fs.Type.Crypto.BKTR:
			self.cryptoType = Fs.Type.Crypto.BKTR
		elif self.cryptoType == Fs.Type.Crypto.NCA0:
			self.cryptoType = Fs.Type.Crypto.NCA0
		elif self.cryptoType == Fs.Type.Crypto.NONE:
			self.cryptoType = Fs.Type.Crypto.NONE
예제 #2
0
def __decompressNcz(nspf, f, statusReportInfo, pleaseNoPrint):
    ncaHeaderSize = 0x4000
    blockID = 0
    nspf.seek(0)
    header = nspf.read(ncaHeaderSize)
    if f != None:
        start = f.tell()

    magic = nspf.read(8)
    if not magic == b'NCZSECTN':
        raise ValueError("No NCZSECTN found! Is this really a .ncz file?")
    sectionCount = nspf.readInt64()
    sections = [Header.Section(nspf) for _ in range(sectionCount)]
    nca_size = ncaHeaderSize
    for i in range(sectionCount):
        nca_size += sections[i].size
    pos = nspf.tell()
    blockMagic = nspf.read(8)
    nspf.seek(pos)
    useBlockCompression = blockMagic == b'NCZBLOCK'
    blockSize = -1
    if useBlockCompression:
        BlockHeader = Header.Block(nspf)
        blockDecompressorReader = BlockDecompressorReader.BlockDecompressorReader(
            nspf, BlockHeader)
    pos = nspf.tell()
    if not useBlockCompression:
        decompressor = ZstdDecompressor().stream_reader(nspf)
    hash = sha256()

    if statusReportInfo == None:
        BAR_FMT = u'{desc}{desc_pad}{percentage:3.0f}%|{bar}| {count:{len_total}d}/{total:d} {unit} [{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]'
        bar = enlighten.Counter(total=nca_size // 1048576,
                                desc='Decompress',
                                unit="MiB",
                                color='red',
                                bar_format=BAR_FMT)
    decompressedBytes = len(header)
    if f != None:
        f.write(header)
    if statusReportInfo != None:
        statusReport, id = statusReportInfo
        statusReport[id] = [len(header), 0, nca_size]
    else:
        bar.count = decompressedBytes // 1048576
        bar.refresh()
    hash.update(header)

    for s in sections:
        i = s.offset
        crypto = aes128.AESCTR(s.cryptoKey, s.cryptoCounter)
        end = s.offset + s.size
        while i < end:
            crypto.seek(i)
            chunkSz = 0x10000 if end - i > 0x10000 else end - i
            if useBlockCompression:
                inputChunk = blockDecompressorReader.read(chunkSz)
            else:
                inputChunk = decompressor.read(chunkSz)
            if not len(inputChunk):
                break
            if not useBlockCompression:
                decompressor.flush()
            if s.cryptoType in (3, 4):
                inputChunk = crypto.encrypt(inputChunk)
            if f != None:
                f.write(inputChunk)
            hash.update(inputChunk)
            lenInputChunk = len(inputChunk)
            i += lenInputChunk
            decompressedBytes += lenInputChunk
            if statusReportInfo != None:
                statusReport[id] = [
                    statusReport[id][0] + chunkSz, statusReport[id][1],
                    nca_size
                ]
            else:
                bar.count = decompressedBytes // 1048576
                bar.refresh()

    bar.close()
    hexHash = hash.hexdigest()
    if f != None:
        end = f.tell()
        written = (end - start)
        return (written, hexHash)
    return (0, hexHash)
예제 #3
0
def __decompressNcz(nspf, f, statusReportInfo, pleaseNoPrint):
	UNCOMPRESSABLE_HEADER_SIZE = 0x4000
	blockID = 0
	nspf.seek(0)
	header = nspf.read(UNCOMPRESSABLE_HEADER_SIZE)
	if f != None:
		start = f.tell()

	magic = nspf.read(8)
	if not magic == b'NCZSECTN':
		raise ValueError("No NCZSECTN found! Is this really a .ncz file?")
	sectionCount = nspf.readInt64()
	sections = [Header.Section(nspf) for _ in range(sectionCount)]
	if sections[0].offset-UNCOMPRESSABLE_HEADER_SIZE > 0:
		fakeSection = Header.FakeSection(UNCOMPRESSABLE_HEADER_SIZE, sections[0].offset-UNCOMPRESSABLE_HEADER_SIZE)
		sections.insert(0, fakeSection)
	nca_size = UNCOMPRESSABLE_HEADER_SIZE
	for i in range(sectionCount):
		nca_size += sections[i].size
	pos = nspf.tell()
	blockMagic = nspf.read(8)
	nspf.seek(pos)
	useBlockCompression = blockMagic == b'NCZBLOCK'
	blockSize = -1
	if useBlockCompression:
		BlockHeader = Header.Block(nspf)
		blockDecompressorReader = BlockDecompressorReader.BlockDecompressorReader(nspf, BlockHeader)
	pos = nspf.tell()
	if not useBlockCompression:
		decompressor = ZstdDecompressor().stream_reader(nspf)
	hash = sha256()
	
	if statusReportInfo == None:
		BAR_FMT = u'{desc}{desc_pad}{percentage:3.0f}%|{bar}| {count:{len_total}d}/{total:d} {unit} [{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]'
		bar = enlighten.Counter(total=nca_size//1048576, desc='Decompress', unit="MiB", color='red', bar_format=BAR_FMT)
	decompressedBytes = len(header)
	if f != None:
		f.write(header)
	if statusReportInfo != None:
		statusReport, id = statusReportInfo
		statusReport[id] = [len(header), 0, nca_size]
	else:
		bar.count = decompressedBytes//1048576
		bar.refresh()
	hash.update(header)

	firstSection = True
	for s in sections:
		i = s.offset
		useCrypto = s.cryptoType in (3, 4)
		if useCrypto:
			crypto = aes128.AESCTR(s.cryptoKey, s.cryptoCounter)
		end = s.offset + s.size
		if firstSection:
			firstSection = False
			uncompressedSize = UNCOMPRESSABLE_HEADER_SIZE-sections[0].offset
			if uncompressedSize > 0:
				i += uncompressedSize
		while i < end:
			if useCrypto:
				crypto.seek(i)
			chunkSz = 0x10000 if end - i > 0x10000 else end - i
			if useBlockCompression:
				inputChunk = blockDecompressorReader.read(chunkSz)
			else:
				inputChunk = decompressor.read(chunkSz)
				decompressor.flush()
			if not len(inputChunk):
				break
			if useCrypto:
				inputChunk = crypto.encrypt(inputChunk)
			if f != None:
				f.write(inputChunk)
			hash.update(inputChunk)
			lenInputChunk = len(inputChunk)
			i += lenInputChunk
			decompressedBytes += lenInputChunk
			if statusReportInfo != None:
				statusReport[id] = [statusReport[id][0]+chunkSz, statusReport[id][1], nca_size]
			else:
				bar.count = decompressedBytes//1048576
				bar.refresh()

	if statusReportInfo == None:
		bar.close()
		#Line break after closing the process bar is required to prevent
		#the next output from being on the same line as the process bar
		print()
	hexHash = hash.hexdigest()
	if f != None:
		end = f.tell()
		written = (end - start)
		return (written, hexHash)
	return (0, hexHash)
예제 #4
0
def __decompressNcz(nspf, f, statusReportInfo):
    UNCOMPRESSABLE_HEADER_SIZE = 0x4000
    blockID = 0
    nspf.seek(0)
    header = nspf.read(UNCOMPRESSABLE_HEADER_SIZE)
    if f is not None:
        start = f.tell()

    magic = nspf.read(8)
    if not magic == b'NCZSECTN':
        raise ValueError("No NCZSECTN found! Is this really a .ncz file?")
    sectionCount = nspf.readInt64()
    sections = [Section(nspf) for _ in range(sectionCount)]
    if sections[0].offset - UNCOMPRESSABLE_HEADER_SIZE > 0:
        fakeSection = FakeSection(
            UNCOMPRESSABLE_HEADER_SIZE,
            sections[0].offset - UNCOMPRESSABLE_HEADER_SIZE)
        sections.insert(0, fakeSection)
    nca_size = UNCOMPRESSABLE_HEADER_SIZE
    for i in range(sectionCount):
        nca_size += sections[i].size

    decompressor = ZstdDecompressor().stream_reader(nspf)
    hash = sha256()

    bar = Status.create(nspf.size, desc=os.path.basename(nspf._path), unit='B')

    # if statusReportInfo == None:
    #	BAR_FMT = u'{desc}{desc_pad}{percentage:3.0f}%|{bar}| {count:{len_total}d}/{total:d} {unit} [{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]'
    #	bar = enlighten.Counter(total=nca_size//1048576, desc='Decompress', unit="MiB", color='red', bar_format=BAR_FMT)
    decompressedBytes = len(header)
    if f is not None:
        f.write(header)
        bar.add(len(header))

    hash.update(header)

    firstSection = True
    for s in sections:
        i = s.offset
        useCrypto = s.cryptoType in (3, 4)
        if useCrypto:
            crypto = aes128.AESCTR(s.cryptoKey, s.cryptoCounter)
        end = s.offset + s.size
        if firstSection:
            firstSection = False
            uncompressedSize = UNCOMPRESSABLE_HEADER_SIZE - sections[0].offset
            if uncompressedSize > 0:
                i += uncompressedSize
        while i < end:
            if useCrypto:
                crypto.seek(i)
            chunkSz = 0x10000 if end - i > 0x10000 else end - i

            inputChunk = decompressor.read(chunkSz)
            decompressor.flush()

            if not len(inputChunk):
                break
            if useCrypto:
                inputChunk = crypto.encrypt(inputChunk)
            if f is not None:
                f.write(inputChunk)
                bar.add(len(inputChunk))
            hash.update(inputChunk)
            lenInputChunk = len(inputChunk)
            i += lenInputChunk
            decompressedBytes += lenInputChunk
            bar.add(lenInputChunk)

    bar.close()
    print()

    hexHash = hash.hexdigest()
    if f is not None:
        end = f.tell()
        written = (end - start)
        return (written, hexHash)
    return (0, hexHash)
예제 #5
0
def __decompress(filePath, outputDir = None, write = True, raiseVerificationException = False):
	
	ncaHeaderSize = 0x4000
	CHUNK_SZ = 0x100000
	
	if write:
		if outputDir is None:
			nspPath = filePath[0:-1] + 'p'
		else:
			nspPath = os.path.join(outputDir, os.path.basename(filePath[0:-1] + 'p'))
			
		nspPath = os.path.abspath(nspPath)
		
		Print.info('decompressing %s -> %s' % (filePath, nspPath))
		
		newNsp = Fs.Pfs0.Pfs0Stream(nspPath)
	
	filePath = os.path.abspath(filePath)
	container = Fs.factory(filePath)
	
	container.open(filePath, 'rb')
	
	
	for nspf in container:
		if isinstance(nspf, Fs.Nca.Nca) and nspf.header.contentType == Fs.Type.Content.DATA:
			Print.info('skipping delta fragment')
			continue

		if not nspf._path.endswith('.ncz'):
			verifyFile = nspf._path.endswith('.nca') and not nspf._path.endswith('.cnmt.nca')
			if write:
				f = newNsp.add(nspf._path, nspf.size)
			hash = hashlib.sha256()
			nspf.seek(0)
			while not nspf.eof():
				inputChunk = nspf.read(CHUNK_SZ)
				hash.update(inputChunk)
				if write:
					f.write(inputChunk)
			hexHash = hash.hexdigest()[0:32]
			if verifyFile:
				if hexHash + '.nca' == nspf._path:
					Print.error('[VERIFIED]   {0}'.format(nspf._path))
				else:
					Print.info('[CORRUPTED]  {0}'.format(nspf._path))
					if raiseVerificationException:
						raise Exception("Verification detected hash missmatch!")
			elif not write:
				Print.info('[EXISTS]     {0}'.format(nspf._path))
			continue

		newFileName = nspf._path[0:-1] + 'a'
		if write:
			f = newNsp.add(newFileName, nspf.size)
			start = f.tell()
		blockID = 0
		nspf.seek(0)
		
		header = nspf.read(ncaHeaderSize)
		magic = nspf.read(8)
		if not magic == b'NCZSECTN':
			raise ValueError("No NCZSECTN found! Is this really a .ncz file?")
		sectionCount = nspf.readInt64()
		sections = []
		for i in range(sectionCount):
			sections.append(Header.Section(nspf))

		pos = nspf.tell()
		blockMagic = nspf.read(8)
		nspf.seek(pos)
		useBlockCompression = blockMagic == b'NCZBLOCK'
		
		blockSize = -1
		if useBlockCompression:
			BlockHeader = Header.Block(nspf)
			blockDecompressorReader = BlockDecompressorReader.BlockDecompressorReader(nspf, BlockHeader)
		pos = nspf.tell()

		dctx = zstandard.ZstdDecompressor()
		if not useBlockCompression:
			decompressor = dctx.stream_reader(nspf)

		hash = hashlib.sha256()
		with tqdm(total=nspf.size, unit_scale=True, unit="B/s") as bar:
			if write:
				f.write(header)
			bar.update(len(header))
			hash.update(header)
			
			for s in sections:
				i = s.offset
				
				crypto = aes128.AESCTR(s.cryptoKey, s.cryptoCounter)
				end = s.offset + s.size
				
				while i < end:
					crypto.seek(i)
					chunkSz = 0x10000 if end - i > 0x10000 else end - i
					if useBlockCompression:
						inputChunk = blockDecompressorReader.read(chunkSz)
					else:
						inputChunk = decompressor.read(chunkSz)
					
					if not len(inputChunk):
						break
					
					if not useBlockCompression:
						decompressor.flush()
					if s.cryptoType in (3, 4):
						inputChunk = crypto.encrypt(inputChunk)
					if write:
						f.write(inputChunk)
					bar.update(len(inputChunk))
					hash.update(inputChunk)
					
					i += len(inputChunk)

		hexHash = hash.hexdigest()[0:32]
		if hexHash + '.nca' == newFileName:
			Print.error('[VERIFIED]   {0}'.format(nspf._path))
		else:
			Print.info('[CORRUPTED]  {0}'.format(nspf._path))
			if raiseVerificationException:
				raise Exception("Verification detected hash missmatch")

		
		if write:
			end = f.tell()
			written = (end - start)
			newNsp.resize(newFileName, written)
		
		continue

	if write:
		newNsp.close()
예제 #6
0
def decompress(filePath, outputDir = None):
	filePath = os.path.abspath(filePath)
	container = Fs.factory(filePath)
	
	container.open(filePath, 'rb')

	CHUNK_SZ = 0x1000000

	if outputDir is None:
		nspPath = filePath[0:-1] + 'p'
	else:
		nspPath = os.path.join(outputDir, os.path.basename(filePath[0:-1] + 'p'))
		
	nspPath = os.path.abspath(nspPath)
	
	Print.info('decompressing %s -> %s' % (filePath, nspPath))
	
	newNsp = Fs.Pfs0.Pfs0Stream(nspPath)

	for nspf in container:
		if isinstance(nspf, Fs.Nca.Nca) and nspf.header.contentType == Fs.Type.Content.DATA:
			Print.info('skipping delta fragment')
			continue
			
			
		if nspf._path.endswith('.ncz'):
			newFileName = nspf._path[0:-1] + 'a'

			f = newNsp.add(newFileName, nspf.size)
			
			start = nspf.tell()

			nspf.seek(0)
			
			header = nspf.read(ncaHeaderSize)
			magic = nspf.read(8)
			if not magic == b'NCZSECTN':
				raise ValueError("No NCZSECTN found! Is this really a .ncz file?")
			sectionCount = nspf.readInt64()
			sections = []
			for i in range(sectionCount):
				sections.append(Section(nspf))

			pos = nspf.tell()
			blockMagic = nspf.read(8)
			nspf.seek(pos)
			useBlockCompression = blockMagic == b'NCZBLOCK'
			
			blockSize = -1
			if useBlockCompression:
				BlockHeader = Block(nspf)
				if BlockHeader.blockSizeExponent < 14 or BlockHeader.blockSizeExponent > 32:
					raise ValueError("Corrupted NCZBLOCK header: Block size must be between 14 and 32")
				blockSize = 2**BlockHeader.blockSizeExponent
			pos = nspf.tell()

			dctx = zstandard.ZstdDecompressor()
			if not useBlockCompression:
				decompressor = dctx.stream_reader(nspf)

			hash = hashlib.sha256()
			with tqdm(total=nspf.size, unit_scale=True, unit="B/s") as bar:
				f.write(header)
				bar.update(len(header))
				hash.update(header)
				
				for s in sections:
					if s.cryptoType == 1: #plain text
						continue
						
					if s.cryptoType not in (3, 4):
						raise IOError('unknown crypto type: %d' % s.cryptoType)
					
					i = s.offset
					
					crypto = aes128.AESCTR(s.cryptoKey, s.cryptoCounter)
					end = s.offset + s.size
					
					while i < end:
						#f.seek(i)
						crypto.seek(i)
						
						if useBlockCompression:
							decompressor = dctx.stream_reader(nspf)
							inputChunk = decompressor.read(blockSize)
							decompressedBytes += len(inputChunk)
							o.write(inputChunk)
							decompressor.flush()
							o.flush()
							print('Block', str(blockID+1)+'/'+str(BlockHeader.numberOfBlocks))
							pos += BlockHeader.compressedBlockSizeList[blockID]
							nspf.seek(pos)
							blockID += 1
							if(blockID >= len(BlockHeader.compressedBlockSizeList)):
								break
						else:
							chunkSz = 0x10000 if end - i > 0x10000 else end - i
							buf = decompressor.read(chunkSz)
						
						if not len(buf):
							break
						
						#f.seek(i)
						buf = crypto.encrypt(buf)
						f.write(buf)
						bar.update(len(buf))
						hash.update(buf)
						
						i += chunkSz
			
			if useBlockCompression and not decompressedBytes == BlockHeader.decompressedSize:
				Print.error("\nSomething went wrong! decompressedBytes != BlockHeader.decompressedSize:", decompressedBytes, "vs.", BlockHeader.decompressedSize)
			hexHash = hash.hexdigest()[0:32]
			if hexHash + '.nca' != newFileName:
				print(hexHash + '.nca')
				print(newFileName)
				Print.error('\nNCZ verification failed!\n')
			else:
				Print.info('\nNCZ verification successful!\n')

			
			end = f.tell()
			written = end - start

			newNsp.resize(newFileName, written)
			continue

		f = newNsp.add(nspf._path, nspf.size)
		nspf.seek(0)
		while not nspf.eof():
			buffer = nspf.read(CHUNK_SZ)
			f.write(buffer)


	newNsp.close()