diff --git a/LibHac/Crypto.cs b/LibHac/Crypto.cs index 6c8551a8..db1f0932 100644 --- a/LibHac/Crypto.cs +++ b/LibHac/Crypto.cs @@ -21,6 +21,14 @@ namespace LibHac return comp; } + public static byte[] ComputeSha256(byte[] data, int offset, int count) + { + using (SHA256 sha = SHA256.Create()) + { + return sha.ComputeHash(data, offset, count); + } + } + public static void DecryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length) { using (Aes aes = Aes.Create()) diff --git a/LibHac/Save/Header.cs b/LibHac/Save/Header.cs index 17afa2f8..70da2619 100644 --- a/LibHac/Save/Header.cs +++ b/LibHac/Save/Header.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Streams; namespace LibHac.Save { @@ -24,13 +25,17 @@ namespace LibHac.Save public byte[] DuplexMasterA { get; } public byte[] DuplexMasterB { get; } + public Stream MasterHash { get; } + public Validity SignatureValidity { get; } public Validity HeaderHashValidity { get; } public byte[] Data { get; } - public Header(Keyset keyset, BinaryReader reader) + public Header(Keyset keyset, SharedStreamSource streamSource) { + var reader = new BinaryReader(streamSource.CreateStream()); + reader.BaseStream.Position = 0; Data = reader.ReadBytes(0x4000); reader.BaseStream.Position = 0; @@ -65,6 +70,8 @@ namespace LibHac.Save reader.BaseStream.Position = Layout.IvfcMasterHashOffsetB; MasterHashB = reader.ReadBytes((int)Layout.IvfcMasterHashSize); + MasterHash = streamSource.CreateStream(Layout.IvfcMasterHashOffsetA, Layout.IvfcMasterHashSize); + reader.BaseStream.Position = Layout.DuplexMasterOffsetA; DuplexMasterA = reader.ReadBytes((int)Layout.DuplexMasterSize); reader.BaseStream.Position = Layout.DuplexMasterOffsetB; diff --git a/LibHac/Save/Savefile.cs b/LibHac/Save/Savefile.cs index 3ab2f999..172bfe49 100644 --- a/LibHac/Save/Savefile.cs +++ b/LibHac/Save/Savefile.cs @@ -28,33 +28,30 @@ namespace LibHac.Save { SavefileSource = new SharedStreamSource(file); - using (var reader = new BinaryReader(SavefileSource.CreateStream(), Encoding.Default, true)) - { - Header = new Header(keyset, reader); - FsLayout layout = Header.Layout; + Header = new Header(keyset, SavefileSource); + FsLayout layout = Header.Layout; - DataRemapStorage = new RemapStorage(SavefileSource.CreateStream(layout.FileMapDataOffset, layout.FileMapDataSize), - Header.FileRemap, Header.FileMapEntries); + DataRemapStorage = new RemapStorage(SavefileSource.CreateStream(layout.FileMapDataOffset, layout.FileMapDataSize), + Header.FileRemap, Header.FileMapEntries); - DuplexData = InitDuplexStream(DataRemapStorage, Header); + DuplexData = InitDuplexStream(DataRemapStorage, Header); - MetaRemapStorage = new RemapStorage(DuplexData, Header.MetaRemap, Header.MetaMapEntries); + MetaRemapStorage = new RemapStorage(DuplexData, Header.MetaRemap, Header.MetaMapEntries); - Stream journalTable = MetaRemapStorage.OpenStream(layout.JournalTableOffset, layout.JournalTableSize); + Stream journalTable = MetaRemapStorage.OpenStream(layout.JournalTableOffset, layout.JournalTableSize); - MappingEntry[] journalMap = JournalStream.ReadMappingEntries(journalTable, Header.Journal.MainDataBlockCount); + MappingEntry[] journalMap = JournalStream.ReadMappingEntries(journalTable, Header.Journal.MainDataBlockCount); - Stream journalData = DataRemapStorage.OpenStream(layout.JournalDataOffset, - layout.JournalDataSizeB + layout.SizeReservedArea); - var journalStream = new JournalStream(journalData, journalMap, (int)Header.Journal.BlockSize); - JournalStreamSource = new SharedStreamSource(journalStream); + Stream journalData = DataRemapStorage.OpenStream(layout.JournalDataOffset, + layout.JournalDataSizeB + layout.SizeReservedArea); + var journalStream = new JournalStream(journalData, journalMap, (int)Header.Journal.BlockSize); + JournalStreamSource = new SharedStreamSource(journalStream); - IvfcStream = InitIvfcStream(integrityCheckLevel); + IvfcStream = InitIvfcStream(integrityCheckLevel); - SaveFs = new SaveFs(IvfcStream, MetaRemapStorage.OpenStream(layout.FatOffset, layout.FatSize), Header.Save); + SaveFs = new SaveFs(IvfcStream, MetaRemapStorage.OpenStream(layout.FatOffset, layout.FatSize), Header.Save); - IvfcStreamSource = new SharedStreamSource(IvfcStream); - } + IvfcStreamSource = new SharedStreamSource(IvfcStream); } private static LayeredDuplexFs InitDuplexStream(RemapStorage baseStorage, Header header) @@ -95,7 +92,7 @@ namespace LibHac.Save initInfo[0] = new IntegrityVerificationInfo { - Data = new MemoryStream(Header.MasterHashA), + Data = Header.MasterHash, BlockSize = 0, Type = IntegrityStreamType.Save }; @@ -132,18 +129,28 @@ namespace LibHac.Save public bool FileExists(string filename) => SaveFs.FileExists(filename); - public bool SignHeader(Keyset keyset) + public bool CommitHeader(Keyset keyset) { + SharedStream headerStream = SavefileSource.CreateStream(); + + var hashData = new byte[0x3d00]; + + headerStream.Position = 0x300; + headerStream.Read(hashData, 0, hashData.Length); + + byte[] hash = Crypto.ComputeSha256(hashData, 0, hashData.Length); + headerStream.Position = 0x108; + headerStream.Write(hash, 0, hash.Length); + if (keyset.SaveMacKey.IsEmpty()) return false; - var data = new byte[0x200]; + var cmacData = new byte[0x200]; var cmac = new byte[0x10]; - SharedStream headerStream = SavefileSource.CreateStream(); headerStream.Position = 0x100; - headerStream.Read(data, 0, 0x200); + headerStream.Read(cmacData, 0, 0x200); - Crypto.CalculateAesCmac(keyset.SaveMacKey, data, 0, cmac, 0, 0x200); + Crypto.CalculateAesCmac(keyset.SaveMacKey, cmacData, 0, cmac, 0, 0x200); headerStream.Position = 0; headerStream.Write(cmac, 0, 0x10); diff --git a/hactoolnet/ProcessSave.cs b/hactoolnet/ProcessSave.cs index dce56572..3fd3f551 100644 --- a/hactoolnet/ProcessSave.cs +++ b/hactoolnet/ProcessSave.cs @@ -73,7 +73,7 @@ namespace hactoolnet if (ctx.Options.SignSave) { - if (save.SignHeader(ctx.Keyset)) + if (save.CommitHeader(ctx.Keyset)) { ctx.Logger.LogMessage("Successfully signed save file"); }