public IStorage OpenStorageWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorageWithPatch(patchNca, index); NcaFsHeader header = patchNca.GetFsHeader(index); return(CreateVerificationStorage(integrityCheckLevel, header, rawStorage)); }
public IFileSystem OpenFileSystemWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage storage = OpenStorageWithPatch(patchNca, index, integrityCheckLevel); NcaFsHeader header = patchNca.GetFsHeader(index); return(OpenFileSystem(storage, header)); }
public static Validity VerifySection(this Nca nca, Nca patchNca, int index, IProgressReport logger = null, bool quiet = false) { NcaFsHeader sect = nca.GetFsHeader(index); NcaHashType hashType = sect.HashType; if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) { return(Validity.Unchecked); } var stream = nca.OpenStorageWithPatch(patchNca, index, IntegrityCheckLevel.IgnoreOnInvalid) as HierarchicalIntegrityVerificationStorage; if (stream == null) { return(Validity.Unchecked); } if (!quiet) { logger?.LogMessage($"Verifying section {index}..."); } Validity validity = stream.Validate(true, logger); return(validity); }
public IStorage OpenStorageWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorageWithPatch(patchNca, index); NcaFsHeader header = patchNca.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 static Validity ValidateCtrExDecryption(Nca nca, int index) { // The encryption subsection table in an AesCtrEx-encrypted partition contains the length of the entire partition. // The encryption table is always located immediately following the partition data, so the offset value of the encryption // table located in the NCA header should be the same as the size read from the encryption table. Debug.Assert(nca.CanOpenSection(index)); NcaFsPatchInfo header = nca.GetFsHeader(index).GetPatchInfo(); IStorage decryptedStorage = nca.OpenRawStorage(index); Span <byte> buffer = stackalloc byte[sizeof(long)]; decryptedStorage.Read(header.EncryptionTreeOffset + 8, buffer).ThrowIfFailure(); long readDataSize = BinaryPrimitives.ReadInt64LittleEndian(buffer); if (header.EncryptionTreeOffset != readDataSize) { return(Validity.Invalid); } return(Validity.Valid); }
public IStorage OpenRawStorageWithPatch(Nca patchNca, int index) { IStorage patchStorage = patchNca.OpenRawStorage(index); IStorage baseStorage = SectionExists(index) ? OpenRawStorage(index) : new NullStorage(); patchStorage.GetSize(out long patchSize).ThrowIfFailure(); baseStorage.GetSize(out long baseSize).ThrowIfFailure(); NcaFsHeader header = patchNca.GetFsHeader(index); NcaFsPatchInfo patchInfo = header.GetPatchInfo(); if (patchInfo.RelocationTreeSize == 0) { return(patchStorage); } var treeHeader = new BucketTree.Header(); patchInfo.RelocationTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount); var relocationTableStorage = new SubStorage(patchStorage, patchInfo.RelocationTreeOffset, patchInfo.RelocationTreeSize); var cachedTableStorage = new CachedStorage(relocationTableStorage, IndirectStorage.NodeSize, 4, true); var tableNodeStorage = new SubStorage(cachedTableStorage, 0, nodeStorageSize); var tableEntryStorage = new SubStorage(cachedTableStorage, nodeStorageSize, entryStorageSize); var storage = new IndirectStorage(); storage.Initialize(tableNodeStorage, tableEntryStorage, treeHeader.EntryCount).ThrowIfFailure(); storage.SetStorage(0, baseStorage, 0, baseSize); storage.SetStorage(1, patchStorage, 0, patchSize); return(storage); }
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.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); }