def __init__(self, nca3, section_header, verify=False, ifo=0, buffer=65536): self.nca = nca3 self.section_header = section_header self.key = self.nca.body_key self.nonce = pk_u64(self.section_header.section_ctr, endianness='>') self.section_offset = self.section_header.offset self.section_size = self.section_header.size self.fs_type = self.section_header.fs_type self.crypto = self.section_header.crypto_type self.ifo = ifo self.buffer = buffer if self.crypto in ('BTKR', 'XTS'): raise NotImplementedError('BTKR/XTS not implemented') super(NCA3.SectionInNCA3, self).__init__(self.nca.f, self.section_offset, self.section_size) self.is_exefs = False if self.fs_type == 'PFS0': self.cont_offset = self.section_header.superblock.offset self.cont_size = self.section_header.superblock.size self.fs = HashTableWrappedPFS0(self.section_header.superblock, self, verify=verify) if 'main.npdm' in self.fs.files: self.is_exefs = True elif self.fs_type == 'RomFS': self.cont_offset = self.section_header.superblock.lvls[ IVFCSuperblock.IVFC_MAX_LEVEL - 1].offset self.cont_size = self.section_header.superblock.lvls[ IVFCSuperblock.IVFC_MAX_LEVEL - 1].size self.fs = HashTreeWrappedRomFS(self.section_header.superblock, self, verify=verify)
class SectionInNCA3(FileInContainer): def __init__(self, nca3, section_header, verify=False, ifo=0, buffer=65536): self.nca = nca3 self.section_header = section_header self.key = self.nca.body_key self.nonce = pk_u64(self.section_header.section_ctr, endianness='>') self.section_offset = self.section_header.offset self.section_size = self.section_header.size self.fs_type = self.section_header.fs_type self.crypto = self.section_header.crypto_type self.ifo = ifo self.buffer = buffer if self.crypto in ('BTKR', 'XTS'): raise NotImplementedError('BTKR/XTS not implemented') super(NCA3.SectionInNCA3, self).__init__(self.nca.f, self.section_offset, self.section_size) self.is_exefs = False if self.fs_type == 'PFS0': self.cont_offset = self.section_header.superblock.offset self.cont_size = self.section_header.superblock.size self.fs = HashTableWrappedPFS0(self.section_header.superblock, self, verify=verify) if 'main.npdm' in self.fs.files: self.is_exefs = True elif self.fs_type == 'RomFS': self.cont_offset = self.section_header.superblock.lvls[ IVFCSuperblock.IVFC_MAX_LEVEL - 1].offset self.cont_size = self.section_header.superblock.lvls[ IVFCSuperblock.IVFC_MAX_LEVEL - 1].size self.fs = HashTreeWrappedRomFS(self.section_header.superblock, self, verify=verify) def update_ctr(self, off): self.ctr = Counter.new( 64, prefix=self.nonce, initial_value=((self.section_offset + off) >> 4)) self.cipher = AES.new(self.key, AES.MODE_CTR, counter=self.ctr) def seek(self, off): if self.crypto == 'None' or self.crypto == 'BTKR' or self.crypto == 'XTS': super(NCA3.SectionInNCA3, self).seek(self.ifo + off) elif self.crypto == 'CTR': block_offset = off & ~0xF self.sector_offset = off & 0xF super(NCA3.SectionInNCA3, self).seek(self.ifo + block_offset) self.update_ctr(block_offset) def read(self, length=None): if self.crypto == 'None' or self.crypto == 'BTKR' or self.crypto == 'XTS': data = super(NCA3.SectionInNCA3, self).read(length) return data elif self.crypto == 'CTR': aligned_length = align_to(length + self.sector_offset, 0x10) dec_data = self.cipher.decrypt( super(NCA3.SectionInNCA3, self).read(aligned_length)) if aligned_length != length: try: return dec_data[self.sector_offset:length + self.sector_offset] finally: # Seek to what the offset should be if not for the crypto self.seek(self.ifo + self.tell() - aligned_length + length) else: return dec_data[:length] def _decrypt_from_offset(self, off, size=None): self.seek(off + self.ifo) read = 0 while True: buf = self.read(self.buffer) if not buf: break if size and (read + len(buf) >= size): yield buf[:size - read] break yield buf read += len(buf) def decrypt_raw(self): for buf in self._decrypt_from_offset(0): yield buf def decrypt_raw_cont(self): for buf in self._decrypt_from_offset(self.cont_offset, self.cont_size): yield buf def extract_cont(self, dest=None): self.fs.extract(dest, disp=False)