def verify(self): ncaHeader = NcaHeader() ncaHeader.open( MemoryFile(self.ncaHeader, Type.Crypto.XTS, uhx(Keys.get('header_key')))) id = ncaHeader.rightsId[0:16].decode().upper() if str(self.titleId) != id: raise IndexError('Title IDs do not match! ' + str(self.titleId) + ' != ' + id) decKey = Keys.decryptTitleKey(uhx(self.titleKey), ncaHeader.masterKey) pfs0 = Fs.Pfs0(self.sectionHeaderBlock) ''' print('encKey = ' + str(self.titleKey)) print('decKey = ' + str(hx(decKey))) print('master key = ' + str(ncaHeader.masterKey)) print('ctr = ' + str(hx(pfs0.cryptoCounter))) print('offset = ' + str(self.pfs0Offset)) ''' if self.sectionHeaderBlock[8:12] == b'IVFC': #Hex.dump(self.sectionHeaderBlock) #Print.info(hx(self.sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8')) mem = MemoryFile(self.pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset=self.pfs0Offset) data = mem.read() #Hex.dump(data) #print('hash = %s' % str(_sha256(data))) if hx(self.sectionHeaderBlock[0xc8:0xc8 + 0x20]).decode('utf-8') == str( _sha256(data)): return True else: return False else: mem = MemoryFile(self.pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset=self.pfs0Offset) magic = mem.read()[0:4] if magic != b'PFS0': raise LookupError('Title Key is incorrect!') return True
def restore(self): header = self.getVerifiedHeader() if not header: raise IOError('could not restore nca header for %s - %s' % (self.titleId, str(self.f._path))) if not self.hasTitleRights(): if header[0x30:0x40] != b'\x00' * 0x10: self.rightsId = hx(header[0x30:0x40]) key = hx( Keys.encryptTitleKey( self.key(), max(max(header[0x6], header[0x20]) - 1, 0))).decode().upper() #key = hx(self.key()).decode().upper() title = Titles.get(self.titleId) if title.key and title.key != key: raise IOError( 'nca title key does not match database: %s vs %s' % (title.key, key)) elif not title.key: title.key = key else: self.rightsId = b'0' * 32 self.seek(0x200) self.write(header) return True
def printInfo(self, maxDepth=3, indent=0): tabs = '\t' * indent try: rightsId = format(self.getRightsId(), 'X').zfill(32) except: Print.info('\n%sInvalid Ticket\n' % (tabs)) return titleId = rightsId[0:16] titleKey = format(self.getTitleKeyBlock(), 'X').zfill(32) Print.info('\n%sTicket\n' % (tabs)) super(Ticket, self).printInfo(maxDepth, indent) Print.info(tabs + 'signatureType = ' + str(self.signatureType)) Print.info(tabs + 'keyType = ' + str(self.keyType)) Print.info(tabs + 'masterKeyRev = ' + str(self.masterKeyRevision)) Print.info(tabs + 'ticketId = ' + str(self.ticketId)) Print.info(tabs + 'deviceId = ' + str(self.deviceId)) Print.info(tabs + 'rightsId = ' + rightsId) Print.info(tabs + 'accountId = ' + str(self.accountId)) Print.info(tabs + 'titleId = ' + titleId) Print.info(tabs + 'titleKey = ' + titleKey) try: Print.info(tabs + 'titleKeyDec = ' + str( hx( Keys.decryptTitleKey( (self.getTitleKey()), self.masterKeyRevision)))) except BaseException as e: Print.info(tabs + 'titleKeyDec = ' + str(e)) '''
def readFile(self, filePath): try: cal0File = open(filePath, 'rb') cal0 = cal0File.read() if int.from_bytes(cal0[0x0:0x4], byteorder='little', signed=False) == 810303811: self.serialNumber = cal0[0x250:0x25E].decode('utf-8') sslCertificateSize = int.from_bytes(cal0[0xAD0:0xAD4], byteorder='little', signed=False) self.sslCertificate = cal0[0xAE0:0xAE0 + sslCertificateSize] self.rsa2048ETicketCertificate = cal0[0x2A90:0x2CD0] self.deviceId = hexlify(cal0[0x35E0:0x35E8]).decode('utf-8') self.extendedRsa2048ETicketKey = cal0[0x3890:0x3AD0] ctr = Counter.new(128, initial_value=int( hexlify(cal0[0x3AE0:0x3AF0]), 16)) dec = AES.new(Keys.getKey('ssl_rsa_kek'), AES.MODE_CTR, counter=ctr).decrypt(cal0[0x3AF0:0x3C10]) self.extendedSslKey = get_priv_key_der(self.sslCertificate, dec[:0x100]) return True else: print('File specified is not a valid CAL0 file') cal0File.close() except FileNotFoundError: print('CAL0 specified not found! Unable to read information') return False
def printInfo(self, maxDepth=3, indent=0): tabs = '\t' * indent rightsId = format(self.getRightsId(), 'X').zfill(32) titleId = rightsId[0:16] titleKey = format(self.getTitleKeyBlock(), 'X').zfill(32) Print.info('\n%sTicket\n' % (tabs)) super(Ticket, self).printInfo(maxDepth, indent) Print.info(tabs + 'signatureType = ' + str(self.signatureType)) Print.info(tabs + 'keyType = ' + str(self.keyType)) Print.info(tabs + 'masterKeyRev = ' + str(self.masterKeyRevision)) Print.info(tabs + 'ticketId = ' + str(self.ticketId)) Print.info(tabs + 'deviceId = ' + str(self.deviceId)) Print.info(tabs + 'rightsId = ' + rightsId) Print.info(tabs + 'accountId = ' + str(self.accountId)) Print.info(tabs + 'titleId = ' + titleId) Print.info(tabs + 'titleKey = ' + titleKey) Print.info(tabs + 'titleKeyDec = ' + str( hx( Keys.decryptTitleKey( (self.getTitleKey()), self.masterKeyRevision)))) try: if blockchain.verifyKey(titleId, titleKey): tkeyStatus = 'VERIFIED' else: tkeyStatus = 'BAD KEY' except BaseException as e: tkeyStatus = 'UNKNOWN - ' + str(e) raise Print.info(tabs + 'titleKeyStatus = ' + tkeyStatus)
def getVerifiedHeader(self): self.seek(0x200) buffer = bytearray(self.read(0x200)) if self.verifyBuffer(buffer): return buffer for gameCardValue in [0, 1]: buffer[0x04] = gameCardValue if self.verifyBuffer(buffer): Print.info('isGameCard = %d' % gameCardValue) return buffer if self.hasTitleRights(): return None title = Titles.get(self.titleId) ''' if title.rightsId: for gameCardValue in [0, 1]: buffer[0x04] = gameCardValue #return False ''' for gameCardValue in [0, 1]: buffer[0x04] = gameCardValue for keyGen in Keys.getKeyGens(): buffer = self.setRightsIdBuffer(buffer, keyGen) if self.verifyBuffer(buffer): Print.info('Title Rights: isGameCard = %d, keyGen = %d' % (gameCardValue, keyGen)) return buffer for gameCardValue in [0, 1]: buffer[0x04] = gameCardValue for keyGen in Keys.getKeyGens(): buffer = self.setStandardCryptoBuffer(buffer, keyGen) if self.verifyBuffer(buffer): Print.info( 'Standard Crypto: isGameCard = %d, keyGen = %d' % (gameCardValue, keyGen)) return buffer return None
def open(self, file=None, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1): super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter) self.header = NcaHeader() self.partition(0x0, 0xC00, self.header, Fs.Type.Crypto.XTS, uhx(Keys.get('header_key'))) #Print.info('partition complete, seeking') self.header.seek(0x400) # Print.info('reading') # Hex.dump(self.header.read(0x200)) # exit() if self._path is not None and self._path.endswith('.ncz'): return for i in range(4): hdr = self.header.read(0x200) section = BaseFs(hdr, cryptoKey=self.header.titleKeyDec) fs = GetSectionFilesystem(hdr, cryptoKey=-1) #Print.info('fs type = ' + hex(fs.fsType)) #Print.info('fs crypto = ' + hex(fs.cryptoType)) #Print.info('st end offset = ' + str(self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset)) #Print.info('fs offset = ' + hex(self.header.sectionTables[i].offset)) #Print.info('fs section start = ' + hex(fs.sectionStart)) #Print.info('titleKey = ' + hex(self.header.titleKeyDec)) self.partition(self.header.sectionTables[i].offset, self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset, section, cryptoKey=self.header.titleKeyDec) try: section.partition(fs.sectionStart, section.size - fs.sectionStart, fs) except BaseException as e: pass # Print.info(e) # raise if fs.fsType: self.sectionFilesystems.append(fs) self.sections.append(section) try: fs.open(None, 'rb') except BaseException as e: Print.error(str(e)) traceback.print_exc(file=sys.stdout) self.titleKeyDec = None
def setStandardCryptoBuffer(self, buffer, keyGen): buffer[0x30:0x40] = b'\x00' * 0x10 emptyKey = b'\x00' * 0x10 kek = Keys.keyAreaKey(Keys.getMasterKeyIndex(keyGen), self.keyIndex) crypto = aes128.AESECB(kek) encKeyBlock = crypto.encrypt(emptyKey + emptyKey + self.titleKeyDec + emptyKey) buffer[0x100:0x140] = encKeyBlock if keyGen <= 2: buffer[0x6] = keyGen buffer[0x20] = 0 else: buffer[0x6] = 2 buffer[0x20] = keyGen return buffer
def removeTitleRights(self): if not Titles.contains(self.titleId): raise IOError('No title key found in database! ' + self.titleId) ticket = self.ticket() masterKeyRev = ticket.getMasterKeyRevision() titleKeyDec = Keys.decryptTitleKey( ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev)) rightsId = ticket.getRightsId() Print.info('rightsId =\t' + hex(rightsId)) Print.info('titleKeyDec =\t' + str(hx(titleKeyDec))) Print.info('masterKeyRev =\t' + hex(masterKeyRev)) for nca in self: if type(nca) == Nca: if nca.header.getCryptoType2() != masterKeyRev: pass raise IOError('Mismatched masterKeyRevs!') ticket.setRightsId(0) for nca in self: if type(nca) == Nca: if nca.header.getRightsId() == 0: continue kek = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex) Print.info('writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev)) Print.info('kek =\t' + hx(kek).decode()) crypto = aes128.AESECB(kek) encKeyBlock = crypto.encrypt(titleKeyDec * 4) nca.header.setRightsId(0) nca.header.setKeyBlock(encKeyBlock) Hex.dump(encKeyBlock)
def open(self, file=None, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1): super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter) self.header = NcaHeader() self.partition(0x0, 0xC00, self.header, Fs.Type.Crypto.XTS, uhx(Keys.get('header_key'))) #Print.info('partition complete, seeking') self.header.seek(0x400) #Print.info('reading') #Hex.dump(self.header.read(0x200)) #exit() for i in range(4): fs = GetSectionFilesystem(self.header.read(0x200), cryptoKey=self.header.titleKeyDec) #Print.info('fs type = ' + hex(fs.fsType)) #Print.info('fs crypto = ' + hex(fs.cryptoType)) #Print.info('st end offset = ' + str(self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset)) #Print.info('fs offset = ' + hex(self.header.sectionTables[i].offset)) #Print.info('fs section start = ' + hex(fs.sectionStart)) #Print.info('titleKey = ' + hex(self.header.titleKeyDec)) try: self.partition(self.header.sectionTables[i].offset + fs.sectionStart, self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset, fs, cryptoKey=self.header.titleKeyDec) except BaseException as e: pass #Print.info(e) #raise if fs.fsType: self.sectionFilesystems.append(fs) self.titleKeyDec = None self.masterKey = None
def open(self, file=None, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1): super(NcaHeader, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter) self.rewind() self.signature1 = self.read(0x100) self.signature2 = self.read(0x100) self.magic = self.read(0x4) self.isGameCard = self.readInt8() self.contentType = self.readInt8() try: self.contentType = Fs.Type.Content(self.contentType) except: pass self.cryptoType = self.readInt8() self.keyIndex = self.readInt8() self.size = self.readInt64() self.titleId = hx(self.read(8)[::-1]).decode('utf-8').upper() self.contentIndex = self.readInt32() self.sdkVersion = self.readInt32() self.cryptoType2 = self.readInt8() self.read(0xF) # padding self.rightsId = hx(self.read(0x10)) if self.magic not in [b'NCA3', b'NCA2']: raise Exception('Failed to decrypt NCA header: ' + str(self.magic)) self.sectionHashes = [] for i in range(4): self.sectionTables.append(SectionTableEntry(self.read(0x10))) for i in range(4): self.sectionHashes.append(self.sectionTables[i]) self.masterKey = (self.cryptoType if self.cryptoType > self.cryptoType2 else self.cryptoType2) - 1 if self.masterKey < 0: self.masterKey = 0 self.encKeyBlock = self.getKeyBlock() #for i in range(4): # offset = i * 0x10 # key = encKeyBlock[offset:offset+0x10] # Print.info('enc %d: %s' % (i, hx(key))) #crypto = aes128.AESECB(Keys.keyAreaKey(self.masterKey, 0)) self.keyBlock = Keys.unwrapAesWrappedTitlekey(self.encKeyBlock, self.masterKey) self.keys = [] for i in range(4): offset = i * 0x10 key = self.keyBlock[offset:offset + 0x10] #Print.info('dec %d: %s' % (i, hx(key))) self.keys.append(key) if self.hasTitleRights(): titleRightsTitleId = self.rightsId.decode()[0:16].upper() if titleRightsTitleId in Titles.keys() and Titles.get( titleRightsTitleId).key: self.titleKeyDec = Keys.decryptTitleKey( uhx(Titles.get(titleRightsTitleId).key), self.masterKey) else: Print.info('could not find title key!') else: self.titleKeyDec = self.key() return True
def verifyKey(self, userkey): if not self.header.hasTitleRights(): titleKeyDec = Keys.decryptTitleKey( file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(self.masterKey())) else: encKey = userkey titleKeyDec = Keys.decryptTitleKey( encKey, Keys.getMasterKeyIndex(self.masterKey())) ''' print('\nTesting {} with:'.format(self)) print('- Keygeneration {}'.format(self.masterKey())) print('- Encrypted key {}'.format(str(hx(encKey))[2:-1])) print('- Decrypted key {}'.format(str(hx(titleKeyDec))[2:-1])) ''' decKey = titleKeyDec f = self if self.header.getRightsId() != 0: for fs in self: # print(fs.fsType) # print(fs.cryptoType) if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR: f.seek(0) ncaHeader = NcaHeader() ncaHeader.open( MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key')))) pfs0 = fs sectionHeaderBlock = fs.buffer #fs.f.setKey(b'\x00' * 16) #print('- Current key {}'.format(str(hx(fs.f.cryptoKey))[2:-1])) fs.seek(0) pfs0Offset = 0 # int.from_bytes(sectionHeaderBlock[0x38:0x40], byteorder='little', signed=False) pfs0Header = fs.read(0x10) # Hex.dump(sectionHeaderBlock) #mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset) data = pfs0Header # mem.read(); # Hex.dump(pfs0Header) magic = data[0:4] # print(magic) if magic != b'PFS0': return False else: return True if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.CTR: f.seek(0) ncaHeader = NcaHeader() ncaHeader.open( MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key')))) ncaHeader = f.read(0x400) pfs0 = fs sectionHeaderBlock = fs.buffer levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False) levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False) pfs0Offset = levelOffset f.seek(pfs0Offset + fs.f.offset) pfs0Header = f.read(levelSize) # fs.seek(pfs0Offset) #pfs0Header = fs.read(levelSize) #print(sectionHeaderBlock[8:12] == b'IVFC') if sectionHeaderBlock[8:12] == b'IVFC': # Hex.dump(sectionHeaderBlock) # Print.info(hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8')) mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset=fs.f.offset) data = mem.read() #Hex.dump(data, 48) #print('hash = %s' % str(sha256(data).hexdigest())) if hx(sectionHeaderBlock[0xc8:0xc8 + 0x20]).decode('utf-8') == str( sha256(data).hexdigest()): return True else: return False else: mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset=pfs0Offset) data = mem.read() # Hex.dump(data) magic = mem.read()[0:4] # print(magic) if magic != b'PFS0': pass else: return True if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.BKTR and str( f.header.contentType) == 'Content.PROGRAM': f.seek(0) ncaHeader = NcaHeader() ncaHeader.open( MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key')))) ncaHeader = f.read(0x400) pfs0 = fs sectionHeaderBlock = fs.buffer levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False) levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False) pfs0Offset = fs.offset + levelOffset f.seek(pfs0Offset) pfs0Header = f.read(levelSize) if sectionHeaderBlock[8:12] == b'IVFC': for i in range(10): ini = 0x100 + (i * 0x10) fin = 0x110 + (i * 4) test = sectionHeaderBlock[ini:fin] if test == b'BKTR': return True return False
def verifyBuffer(self, buffer): if Keys.pssVerify(buffer, self.signature1, Keys.ncaHdrFixedKeyModulus): return True return False
def setMasterKeyRev(self, newMasterKeyRev): if not Titles.contains(self.titleId): raise IOError('No title key found in database! ' + self.titleId) ticket = self.ticket() masterKeyRev = ticket.getMasterKeyRevision() titleKey = ticket.getTitleKeyBlock() newTitleKey = Keys.changeTitleKeyMasterKey( titleKey.to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev), Keys.getMasterKeyIndex(newMasterKeyRev)) rightsId = ticket.getRightsId() if rightsId != 0: raise IOError('please remove titlerights first') if (newMasterKeyRev == None and rightsId == 0) or masterKeyRev == newMasterKeyRev: Print.info('Nothing to do') return Print.info('rightsId =\t' + hex(rightsId)) Print.info('titleKey =\t' + str(hx(titleKey.to_bytes(16, byteorder='big')))) Print.info('newTitleKey =\t' + str(hx(newTitleKey))) Print.info('masterKeyRev =\t' + hex(masterKeyRev)) for nca in self: if type(nca) == Nca: if nca.header.getCryptoType2() != masterKeyRev: pass raise IOError('Mismatched masterKeyRevs!') ticket.setMasterKeyRevision(newMasterKeyRev) ticket.setRightsId((ticket.getRightsId() & 0xFFFFFFFFFFFFFFFF0000000000000000) + newMasterKeyRev) ticket.setTitleKeyBlock(int.from_bytes(newTitleKey, 'big')) for nca in self: if type(nca) == Nca: if nca.header.getCryptoType2() != newMasterKeyRev: Print.info('writing masterKeyRev for %s, %d -> %s' % (str(nca._path), nca.header.getCryptoType2(), str(newMasterKeyRev))) encKeyBlock = nca.header.getKeyBlock() if sum(encKeyBlock) != 0: key = Keys.keyAreaKey( Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex) Print.info('decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)) crypto = aes128.AESECB(key) decKeyBlock = crypto.decrypt(encKeyBlock) key = Keys.keyAreaKey( Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex) Print.info('encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex)) crypto = aes128.AESECB(key) reEncKeyBlock = crypto.encrypt(decKeyBlock) nca.header.setKeyBlock(reEncKeyBlock) if newMasterKeyRev >= 3: nca.header.setCryptoType(2) nca.header.setCryptoType2(newMasterKeyRev) else: nca.header.setCryptoType(newMasterKeyRev) nca.header.setCryptoType2(0)