From 24e6434765b1752e603a09f73be03449b3b20bd5 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 9 Oct 2018 15:33:56 -0500 Subject: [PATCH] Use an enum instead of a bool for integrity checking levels --- ...HierarchicalIntegrityVerificationStream.cs | 8 +- LibHac/IntegrityVerificationStream.cs | 27 +++++- LibHac/Nca.cs | 83 ++++++++++--------- LibHac/Savefile/Savefile.cs | 8 +- LibHac/SwitchFs.cs | 6 +- NandReader/Program.cs | 2 +- NandReaderGui/ViewModel/NandViewModel.cs | 2 +- hactoolnet/Options.cs | 3 + hactoolnet/ProcessNca.cs | 14 ++-- hactoolnet/ProcessSave.cs | 2 +- hactoolnet/ProcessSwitchFs.cs | 8 +- hactoolnet/ProcessXci.cs | 8 +- 12 files changed, 100 insertions(+), 71 deletions(-) diff --git a/LibHac/HierarchicalIntegrityVerificationStream.cs b/LibHac/HierarchicalIntegrityVerificationStream.cs index bdb09c2b..5a5adad8 100644 --- a/LibHac/HierarchicalIntegrityVerificationStream.cs +++ b/LibHac/HierarchicalIntegrityVerificationStream.cs @@ -8,18 +8,18 @@ namespace LibHac { public Stream[] Levels { get; } public Stream DataLevel { get; } - public bool EnableIntegrityChecks { get; } + public IntegrityCheckLevel IntegrityCheckLevel { get; } - public HierarchicalIntegrityVerificationStream(IntegrityVerificationInfo[] levelInfo, bool enableIntegrityChecks) + public HierarchicalIntegrityVerificationStream(IntegrityVerificationInfo[] levelInfo, IntegrityCheckLevel integrityCheckLevel) { Levels = new Stream[levelInfo.Length]; - EnableIntegrityChecks = enableIntegrityChecks; + IntegrityCheckLevel = integrityCheckLevel; Levels[0] = levelInfo[0].Data; for (int i = 1; i < Levels.Length; i++) { - var levelData = new IntegrityVerificationStream(levelInfo[i], Levels[i - 1], enableIntegrityChecks); + var levelData = new IntegrityVerificationStream(levelInfo[i], Levels[i - 1], integrityCheckLevel); Levels[i] = new RandomAccessSectorStream(levelData); } diff --git a/LibHac/IntegrityVerificationStream.cs b/LibHac/IntegrityVerificationStream.cs index 2c835aab..9057cad5 100644 --- a/LibHac/IntegrityVerificationStream.cs +++ b/LibHac/IntegrityVerificationStream.cs @@ -10,7 +10,7 @@ namespace LibHac private const int DigestSize = 0x20; private Stream HashStream { get; } - public bool EnableIntegrityChecks { get; } + public IntegrityCheckLevel IntegrityCheckLevel { get; } private byte[] Salt { get; } private IntegrityStreamType Type { get; } @@ -18,11 +18,11 @@ namespace LibHac private readonly byte[] _hashBuffer = new byte[DigestSize]; private readonly SHA256 _hash = SHA256.Create(); - public IntegrityVerificationStream(IntegrityVerificationInfo info, Stream hashStream, bool enableIntegrityChecks) + public IntegrityVerificationStream(IntegrityVerificationInfo info, Stream hashStream, IntegrityCheckLevel integrityCheckLevel) : base(info.Data, info.BlockSize) { HashStream = hashStream; - EnableIntegrityChecks = enableIntegrityChecks; + IntegrityCheckLevel = integrityCheckLevel; Salt = info.Salt; Type = info.Type; } @@ -84,7 +84,7 @@ namespace LibHac } } - if (!EnableIntegrityChecks) return bytesRead; + if (IntegrityCheckLevel == IntegrityCheckLevel.None) return bytesRead; _hash.Initialize(); @@ -139,4 +139,23 @@ namespace LibHac RomFs, PartitionFs } + + /// + /// Represents the level of integrity checks to be performed. + /// + public enum IntegrityCheckLevel + { + /// + /// No integrity checks will be performed. + /// + None, + /// + /// + /// + WarnOnInvalid, + /// + /// An will be thrown if an integrity check fails. + /// + ErrorOnInvalid + } } diff --git a/LibHac/Nca.cs b/LibHac/Nca.cs index ff2c00de..eab91d70 100644 --- a/LibHac/Nca.cs +++ b/LibHac/Nca.cs @@ -140,7 +140,7 @@ namespace LibHac false); if (BaseNca == null) return rawStream; - Stream baseStream = BaseNca.OpenSection(ProgramPartitionType.Data, true, false); + Stream baseStream = BaseNca.OpenSection(ProgramPartitionType.Data, true, IntegrityCheckLevel.None); if (baseStream == null) throw new InvalidDataException("Base NCA has no RomFS section"); return new Bktr(rawStream, baseStream, sect); @@ -155,11 +155,11 @@ namespace LibHac /// /// The index of the NCA section to open. Valid indexes are 0-3. /// to open the raw section with hash metadata. - /// to enable data integrity checks when reading the section. - /// Only applies if is . + /// The level of integrity checks to be performed when reading the section. + /// Always if is . /// A that provides access to the specified section. if the section does not exist. /// The specified is outside the valid range. - public Stream OpenSection(int index, bool raw, bool enableIntegrityChecks) + public Stream OpenSection(int index, bool raw, IntegrityCheckLevel integrityCheckLevel) { Stream rawStream = OpenRawSection(index); NcaSection sect = Sections[index]; @@ -173,9 +173,9 @@ namespace LibHac switch (header.HashType) { case NcaHashType.Sha256: - return InitIvfcForPartitionfs(header.Sha256Info, new SharedStreamSource(rawStream), enableIntegrityChecks); + return InitIvfcForPartitionfs(header.Sha256Info, new SharedStreamSource(rawStream), integrityCheckLevel); case NcaHashType.Ivfc: - return InitIvfcForRomfs(header.IvfcInfo, new SharedStreamSource(rawStream), enableIntegrityChecks); + return InitIvfcForRomfs(header.IvfcInfo, new SharedStreamSource(rawStream), integrityCheckLevel); default: throw new ArgumentOutOfRangeException(); @@ -187,15 +187,15 @@ namespace LibHac /// /// The type of section to open. /// to open the raw section with hash metadata. - /// to enable data integrity checks when reading the section. - /// Only applies if is . + /// The level of integrity checks to be performed when reading the section. + /// Always if is . /// A that provides access to the specified section. if the section does not exist. /// The specified is outside the valid range. - public Stream OpenSection(ProgramPartitionType type, bool raw, bool enableIntegrityChecks) => - OpenSection((int)type, raw, enableIntegrityChecks); + public Stream OpenSection(ProgramPartitionType type, bool raw, IntegrityCheckLevel integrityCheckLevel) => + OpenSection((int)type, raw, integrityCheckLevel); private static HierarchicalIntegrityVerificationStream InitIvfcForRomfs(IvfcHeader ivfc, - SharedStreamSource romfsStreamSource, bool enableIntegrityChecks) + SharedStreamSource romfsStreamSource, IntegrityCheckLevel integrityCheckLevel) { var initInfo = new IntegrityVerificationInfo[ivfc.NumLevels]; @@ -219,11 +219,11 @@ namespace LibHac }; } - return new HierarchicalIntegrityVerificationStream(initInfo, enableIntegrityChecks); + return new HierarchicalIntegrityVerificationStream(initInfo, integrityCheckLevel); } private static Stream InitIvfcForPartitionfs(Sha256Info sb, - SharedStreamSource pfsStreamSource, bool enableIntegrityChecks) + SharedStreamSource pfsStreamSource, IntegrityCheckLevel integrityCheckLevel) { SharedStream hashStream = pfsStreamSource.CreateStream(sb.HashTableOffset, sb.HashTableSize); SharedStream dataStream = pfsStreamSource.CreateStream(sb.DataOffset, sb.DataSize); @@ -252,7 +252,7 @@ namespace LibHac Type = IntegrityStreamType.PartitionFs }; - return new HierarchicalIntegrityVerificationStream(initInfo, enableIntegrityChecks); + return new HierarchicalIntegrityVerificationStream(initInfo, integrityCheckLevel); } /// @@ -376,7 +376,7 @@ namespace LibHac return; } - Stream stream = OpenSection(index, true, false); + Stream stream = OpenSection(index, true, IntegrityCheckLevel.None); var hashTable = new byte[size]; stream.Position = offset; @@ -385,25 +385,6 @@ namespace LibHac sect.MasterHashValidity = Crypto.CheckMemoryHashTable(hashTable, expected, 0, hashTable.Length); } - public void VerifySection(int index, IProgressReport logger = null) - { - if (Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index)); - - NcaSection sect = Sections[index]; - Stream stream = OpenSection(index, false, true); - logger?.LogMessage($"Verifying section {index}..."); - - switch (sect.Header.HashType) - { - case NcaHashType.Sha256: - break; - case NcaHashType.Ivfc: - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - public void Dispose() { if (!KeepOpen) @@ -444,12 +425,12 @@ namespace LibHac public static class NcaExtensions { - public static void ExportSection(this Nca nca, int index, string filename, bool raw = false, bool verify = false, IProgressReport logger = null) + public static void ExportSection(this Nca nca, int index, string filename, bool raw = false, IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null) { if (index < 0 || index > 3) throw new IndexOutOfRangeException(); if (nca.Sections[index] == null) return; - Stream section = nca.OpenSection(index, raw, verify); + Stream section = nca.OpenSection(index, raw, integrityCheckLevel); string dir = Path.GetDirectoryName(filename); if (!string.IsNullOrWhiteSpace(dir)) Directory.CreateDirectory(dir); @@ -459,13 +440,13 @@ namespace LibHac } } - public static void ExtractSection(this Nca nca, int index, string outputDir, bool verify = false, IProgressReport logger = null) + public static void ExtractSection(this Nca nca, int index, string outputDir, IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null) { if (index < 0 || index > 3) throw new IndexOutOfRangeException(); if (nca.Sections[index] == null) return; NcaSection section = nca.Sections[index]; - Stream stream = nca.OpenSection(index, false, verify); + Stream stream = nca.OpenSection(index, false, integrityCheckLevel); switch (section.Type) { @@ -483,5 +464,31 @@ namespace LibHac break; } } + + public static void VerifySection(this Nca nca, int index, IProgressReport logger = null) + { + if (nca.Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index)); + + NcaSection sect = nca.Sections[index]; + Stream stream = nca.OpenSection(index, false, IntegrityCheckLevel.WarnOnInvalid); + logger?.LogMessage($"Verifying section {index}..."); + + switch (sect.Header.HashType) + { + case NcaHashType.Sha256: + case NcaHashType.Ivfc: + if (stream is HierarchicalIntegrityVerificationStream ivfc) + { + for (int i = 1; i < ivfc.Levels.Length; i++) + { + logger?.LogMessage($" Verifying IVFC Level {i}..."); + ivfc.Levels[i].CopyStream(Stream.Null, ivfc.Levels[i].Length, logger); + } + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } } } diff --git a/LibHac/Savefile/Savefile.cs b/LibHac/Savefile/Savefile.cs index 015e47ac..cee44595 100644 --- a/LibHac/Savefile/Savefile.cs +++ b/LibHac/Savefile/Savefile.cs @@ -41,7 +41,7 @@ namespace LibHac.Savefile public DirectoryEntry[] Directories { get; private set; } private Dictionary FileDict { get; } - public Savefile(Keyset keyset, Stream file, bool enableIntegrityChecks) + public Savefile(Keyset keyset, Stream file, IntegrityCheckLevel integrityCheckLevel) { SavefileSource = new SharedStreamSource(file); @@ -106,7 +106,7 @@ namespace LibHac.Savefile JournalStream = new JournalStream(journalData, journalMap, (int)Header.Journal.BlockSize); JournalStreamSource = new SharedStreamSource(JournalStream); - IvfcStream = InitIvfcStream(enableIntegrityChecks); + IvfcStream = InitIvfcStream(integrityCheckLevel); IvfcStreamSource = new SharedStreamSource(IvfcStream); ReadFileInfo(); @@ -120,7 +120,7 @@ namespace LibHac.Savefile } } - private HierarchicalIntegrityVerificationStream InitIvfcStream(bool enableIntegrityChecks) + private HierarchicalIntegrityVerificationStream InitIvfcStream(IntegrityCheckLevel integrityCheckLevel) { IvfcHeader ivfc = Header.Ivfc; @@ -151,7 +151,7 @@ namespace LibHac.Savefile }; } - return new HierarchicalIntegrityVerificationStream(initInfo, enableIntegrityChecks); + return new HierarchicalIntegrityVerificationStream(initInfo, integrityCheckLevel); } public Stream OpenFile(string filename) diff --git a/LibHac/SwitchFs.cs b/LibHac/SwitchFs.cs index a009ff83..967aa14e 100644 --- a/LibHac/SwitchFs.cs +++ b/LibHac/SwitchFs.cs @@ -126,7 +126,7 @@ namespace LibHac string sdPath = "/" + Util.GetRelativePath(file, SaveDir).Replace('\\', '/'); var nax0 = new Nax0(Keyset, stream, sdPath, false); - save = new Savefile.Savefile(Keyset, nax0.Stream, false); + save = new Savefile.Savefile(Keyset, nax0.Stream, IntegrityCheckLevel.None); } catch (Exception ex) { @@ -147,7 +147,7 @@ namespace LibHac var title = new Title(); // Meta contents always have 1 Partition FS section with 1 file in it - Stream sect = nca.OpenSection(0, false, true); + Stream sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid); var pfs0 = new Pfs(sect); Stream file = pfs0.OpenFile(pfs0.Files[0]); @@ -187,7 +187,7 @@ namespace LibHac { foreach (Title title in Titles.Values.Where(x => x.ControlNca != null)) { - var romfs = new Romfs(title.ControlNca.OpenSection(0, false, true)); + var romfs = new Romfs(title.ControlNca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid)); byte[] control = romfs.GetFile("/control.nacp"); var reader = new BinaryReader(new MemoryStream(control)); diff --git a/NandReader/Program.cs b/NandReader/Program.cs index 37c9d3bf..4718535d 100644 --- a/NandReader/Program.cs +++ b/NandReader/Program.cs @@ -102,7 +102,7 @@ namespace NandReader private static List ReadTickets(Keyset keyset, Stream savefile) { var tickets = new List(); - var save = new Savefile(keyset, savefile, false); + var save = new Savefile(keyset, savefile, IntegrityCheckLevel.None); var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin")); var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin")); diff --git a/NandReaderGui/ViewModel/NandViewModel.cs b/NandReaderGui/ViewModel/NandViewModel.cs index 2f4a1504..feddd0ad 100644 --- a/NandReaderGui/ViewModel/NandViewModel.cs +++ b/NandReaderGui/ViewModel/NandViewModel.cs @@ -84,7 +84,7 @@ namespace NandReaderGui.ViewModel private static List ReadTickets(Keyset keyset, Stream savefile) { var tickets = new List(); - var save = new Savefile(keyset, savefile, false); + var save = new Savefile(keyset, savefile, IntegrityCheckLevel.None); var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin")); var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin")); diff --git a/hactoolnet/Options.cs b/hactoolnet/Options.cs index 1437906b..69d4aa30 100644 --- a/hactoolnet/Options.cs +++ b/hactoolnet/Options.cs @@ -36,6 +36,9 @@ namespace hactoolnet public bool ListRomFs; public bool SignSave; public ulong TitleId; + + public IntegrityCheckLevel IntegrityLevel => + EnableHash ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; } internal enum FileType diff --git a/hactoolnet/ProcessNca.cs b/hactoolnet/ProcessNca.cs index 3d5948e1..51eae229 100644 --- a/hactoolnet/ProcessNca.cs +++ b/hactoolnet/ProcessNca.cs @@ -26,12 +26,12 @@ namespace hactoolnet { if (ctx.Options.SectionOut[i] != null) { - nca.ExportSection(i, ctx.Options.SectionOut[i], ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + nca.ExportSection(i, ctx.Options.SectionOut[i], ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.SectionOutDir[i] != null) { - nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.EnableHash, ctx.Logger); + nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.Validate && nca.Sections[i] != null) @@ -42,7 +42,7 @@ namespace hactoolnet if (ctx.Options.ListRomFs && nca.Sections[1] != null) { - var romfs = new Romfs(nca.OpenSection(1, false, ctx.Options.EnableHash)); + var romfs = new Romfs(nca.OpenSection(1, false, ctx.Options.IntegrityLevel)); foreach (RomfsFile romfsFile in romfs.Files) { @@ -68,12 +68,12 @@ namespace hactoolnet if (ctx.Options.RomfsOut != null) { - nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.RomfsOutDir != null) { - var romfs = new Romfs(nca.OpenSection(section.SectionNum, false, ctx.Options.EnableHash)); + var romfs = new Romfs(nca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } } @@ -90,12 +90,12 @@ namespace hactoolnet if (ctx.Options.ExefsOut != null) { - nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOutDir != null) { - nca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.EnableHash, ctx.Logger); + nca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } } diff --git a/hactoolnet/ProcessSave.cs b/hactoolnet/ProcessSave.cs index 91efb08c..060f3c3c 100644 --- a/hactoolnet/ProcessSave.cs +++ b/hactoolnet/ProcessSave.cs @@ -14,7 +14,7 @@ namespace hactoolnet { using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite)) { - var save = new Savefile(ctx.Keyset, file, ctx.Options.EnableHash); + var save = new Savefile(ctx.Keyset, file, ctx.Options.IntegrityLevel); if (ctx.Options.OutDir != null) { diff --git a/hactoolnet/ProcessSwitchFs.cs b/hactoolnet/ProcessSwitchFs.cs index 74813824..e1e88ae9 100644 --- a/hactoolnet/ProcessSwitchFs.cs +++ b/hactoolnet/ProcessSwitchFs.cs @@ -55,12 +55,12 @@ namespace hactoolnet if (ctx.Options.ExefsOutDir != null) { - title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.EnableHash, ctx.Logger); + title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { - title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } @@ -95,13 +95,13 @@ namespace hactoolnet if (ctx.Options.RomfsOutDir != null) { - var romfs = new Romfs(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.EnableHash)); + var romfs = new Romfs(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { - title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } diff --git a/hactoolnet/ProcessXci.cs b/hactoolnet/ProcessXci.cs index 8edb511a..d2e5aabf 100644 --- a/hactoolnet/ProcessXci.cs +++ b/hactoolnet/ProcessXci.cs @@ -79,12 +79,12 @@ namespace hactoolnet if (ctx.Options.ExefsOutDir != null) { - mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.EnableHash, ctx.Logger); + mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { - mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } @@ -108,13 +108,13 @@ namespace hactoolnet if (ctx.Options.RomfsOutDir != null) { - var romfs = new Romfs(mainNca.OpenSection(romfsSection.SectionNum, false, ctx.Options.EnableHash)); + var romfs = new Romfs(mainNca.OpenSection(romfsSection.SectionNum, false, ctx.Options.IntegrityLevel)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { - mainNca.ExportSection(romfsSection.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger); + mainNca.ExportSection(romfsSection.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } }