public IStorage OpenStorageWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorageWithPatch(patchNca, index); NcaFsHeader header = patchNca.Header.GetFsHeader(index); switch (header.HashType) { case NcaHashType.Sha256: return(InitIvfcForPartitionFs(header.GetIntegrityInfoSha256(), rawStorage, integrityCheckLevel, true)); case NcaHashType.Ivfc: return(InitIvfcForRomFs(header.GetIntegrityInfoIvfc(), rawStorage, integrityCheckLevel, true)); default: throw new ArgumentOutOfRangeException(); } }
private IStorage CreateVerificationStorage(IntegrityCheckLevel integrityCheckLevel, NcaFsHeader header, IStorage rawStorage) { switch (header.HashType) { case NcaHashType.Sha256: return(InitIvfcForPartitionFs(header.GetIntegrityInfoSha256(), rawStorage, integrityCheckLevel, true)); case NcaHashType.Ivfc: // The FS header of an NCA0 section with IVFC verification must be manually skipped if (Header.IsNca0()) { rawStorage = rawStorage.Slice(0x200); } return(InitIvfcForRomFs(header.GetIntegrityInfoIvfc(), rawStorage, integrityCheckLevel, true)); default: throw new ArgumentOutOfRangeException(); } }
public IStorage OpenStorage(int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorage(index); NcaFsHeader header = Header.GetFsHeader(index); if (header.EncryptionType == NcaEncryptionType.AesCtrEx) { return(rawStorage.Slice(0, header.GetPatchInfo().RelocationTreeOffset)); } switch (header.HashType) { case NcaHashType.Sha256: return(InitIvfcForPartitionFs(header.GetIntegrityInfoSha256(), rawStorage, integrityCheckLevel, true)); case NcaHashType.Ivfc: return(InitIvfcForRomFs(header.GetIntegrityInfoIvfc(), rawStorage, integrityCheckLevel, true)); default: throw new ArgumentOutOfRangeException(); } }
public static Validity ValidateSectionMasterHash(this Nca nca, int index) { if (!nca.SectionExists(index)) { throw new ArgumentException(nameof(index), Messages.NcaSectionMissing); } if (!nca.CanOpenSection(index)) { return(Validity.MissingKey); } NcaFsHeader header = nca.Header.GetFsHeader(index); // The base data is needed to validate the hash, so use a trick involving the AES-CTR extended // encryption table to check if the decryption is invalid. // todo: If the patch replaces the data checked by the master hash, use that directly if (header.IsPatchSection()) { if (header.EncryptionType != NcaEncryptionType.AesCtrEx) { return(Validity.Unchecked); } Validity ctrExValidity = ValidateCtrExDecryption(nca, index); return(ctrExValidity == Validity.Invalid ? Validity.Invalid : Validity.Unchecked); } byte[] expectedHash; long offset; long size; switch (header.HashType) { case NcaHashType.Ivfc: NcaFsIntegrityInfoIvfc ivfcInfo = header.GetIntegrityInfoIvfc(); expectedHash = ivfcInfo.MasterHash.ToArray(); offset = ivfcInfo.GetLevelOffset(0); size = 1 << ivfcInfo.GetLevelBlockSize(0); break; case NcaHashType.Sha256: NcaFsIntegrityInfoSha256 sha256Info = header.GetIntegrityInfoSha256(); expectedHash = sha256Info.MasterHash.ToArray(); offset = sha256Info.GetLevelOffset(0); size = sha256Info.GetLevelSize(0); break; default: return(Validity.Unchecked); } IStorage storage = nca.OpenRawStorage(index); var data = new byte[size]; storage.Read(offset, data).ThrowIfFailure(); var actualHash = new byte[Sha256.DigestSize]; Sha256.GenerateSha256Hash(data, actualHash); if (Utilities.ArraysEqual(expectedHash, actualHash)) { return(Validity.Valid); } return(Validity.Invalid); }