diff --git a/src/LibHac/Compatibility/Rsa.cs b/src/LibHac/Compatibility/Rsa.cs index 9f53bb35..96b4c729 100644 --- a/src/LibHac/Compatibility/Rsa.cs +++ b/src/LibHac/Compatibility/Rsa.cs @@ -2,7 +2,7 @@ using System; using System.Numerics; -using System.Security.Cryptography; +using LibHac.Crypto; namespace LibHac.Compatibility { @@ -17,7 +17,6 @@ namespace LibHac.Compatibility const int saltOffset = hashOffset - digestLen; const int padEnd = saltOffset - 1; - SHA256 sha = SHA256.Create(); var message = new byte[rsaLen]; BigInteger decInt = BigInteger.ModPow(CryptoOld.GetBigInteger(signature), new BigInteger(65537), CryptoOld.GetBigInteger(modulus)); @@ -33,9 +32,12 @@ namespace LibHac.Compatibility ref byte seed = ref hashBuf[0x23]; + Span digestBuffer = stackalloc byte[Sha256.DigestSize]; + for (int i = 0; i < hashOffset; i += 0x20) { - Util.XorArrays(message.AsSpan(i, digestLen), sha.ComputeHash(hashBuf)); + Sha256.GenerateSha256Hash(hashBuf, digestBuffer); + Util.XorArrays(message.AsSpan(i, digestLen), digestBuffer); seed++; } @@ -46,14 +48,21 @@ namespace LibHac.Compatibility return false; } - var prefix = new byte[8]; - byte[] digest = sha.ComputeHash(data); + Span prefix = stackalloc byte[8]; + Span digest = stackalloc byte[Sha256.DigestSize]; - sha.TransformBlock(prefix, 0, prefix.Length, null, 0); - sha.TransformBlock(digest, 0, digestLen, null, 0); - sha.TransformFinalBlock(message, saltOffset, digestLen); + Sha256.GenerateSha256Hash(data, digest); - return Util.SpansEqual(hashBuf.AsSpan(0, 0x20), sha.Hash); + IHash sha2 = Sha256.CreateSha256Generator(); + sha2.Initialize(); + + sha2.Update(prefix); + sha2.Update(digest); + sha2.Update(message.AsSpan(saltOffset, digestLen)); + + sha2.GetHash(digest); + + return Util.SpansEqual(hashBuf.AsSpan(0, 0x20), digest); } } } diff --git a/src/LibHac/Crypto/Sha256.cs b/src/LibHac/Crypto/Sha256.cs index 38563a4b..8b5559c0 100644 --- a/src/LibHac/Crypto/Sha256.cs +++ b/src/LibHac/Crypto/Sha256.cs @@ -7,6 +7,10 @@ namespace LibHac.Crypto { public const int DigestSize = 0x20; + /// + /// Creates an uninitialized SHA-256 object. + /// + /// The new uninitialized SHA-256 object. public static IHash CreateSha256Generator() { return new Sha256Generator(); diff --git a/src/LibHac/CryptoOld.cs b/src/LibHac/CryptoOld.cs index 0b05c4f1..bd61a5ea 100644 --- a/src/LibHac/CryptoOld.cs +++ b/src/LibHac/CryptoOld.cs @@ -9,25 +9,6 @@ namespace LibHac public static class CryptoOld { internal const int Aes128Size = 0x10; - internal const int Sha256DigestSize = 0x20; - - public static Validity CheckMemoryHashTable(byte[] data, byte[] hash, int offset, int count) - { - Validity comp; - using (SHA256 sha = SHA256.Create()) - { - comp = Util.ArraysEqual(hash, sha.ComputeHash(data, offset, count)) ? Validity.Valid : Validity.Invalid; - } - 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) { @@ -172,7 +153,7 @@ namespace LibHac public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus) { #if NETFRAMEWORK - if (Compatibility.Env.IsMono) + if (!Compatibility.Env.IsMono) { return Compatibility.Rsa.Rsa2048PssVerifyMono(data, signature, modulus) ? Validity.Valid diff --git a/src/LibHac/FsSystem/IntegrityVerificationStorage.cs b/src/LibHac/FsSystem/IntegrityVerificationStorage.cs index 6f10630b..bff41807 100644 --- a/src/LibHac/FsSystem/IntegrityVerificationStorage.cs +++ b/src/LibHac/FsSystem/IntegrityVerificationStorage.cs @@ -1,7 +1,7 @@ using System; using System.Buffers; using System.IO; -using System.Security.Cryptography; +using LibHac.Crypto; using LibHac.Fs; using LibHac.FsSystem.Save; @@ -18,7 +18,7 @@ namespace LibHac.FsSystem private byte[] Salt { get; } private IntegrityStorageType Type { get; } - private readonly SHA256 _hash = SHA256.Create(); + private readonly IHash _hash = Sha256.CreateSha256Generator(); private readonly object _locker = new object(); public IntegrityVerificationStorage(IntegrityVerificationInfo info, IStorage hashStorage, @@ -170,13 +170,13 @@ namespace LibHac.FsSystem if (Type == IntegrityStorageType.Save) { - _hash.TransformBlock(Salt, 0, Salt.Length, null, 0); + _hash.Update(Salt); } - _hash.TransformBlock(buffer, offset, count, null, 0); - _hash.TransformFinalBlock(buffer, 0, 0); + _hash.Update(buffer.AsSpan(offset, count)); - byte[] hash = _hash.Hash; + var hash = new byte[Sha256.DigestSize]; + _hash.GetHash(hash); if (Type == IntegrityStorageType.Save) { diff --git a/src/LibHac/FsSystem/NcaUtils/NcaExtensions.cs b/src/LibHac/FsSystem/NcaUtils/NcaExtensions.cs index 0f0e4e36..8b906765 100644 --- a/src/LibHac/FsSystem/NcaUtils/NcaExtensions.cs +++ b/src/LibHac/FsSystem/NcaUtils/NcaExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Buffers.Binary; using System.Diagnostics; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac.FsSystem.NcaUtils @@ -98,7 +99,8 @@ namespace LibHac.FsSystem.NcaUtils var data = new byte[size]; storage.Read(offset, data).ThrowIfFailure(); - byte[] actualHash = CryptoOld.ComputeSha256(data, 0, data.Length); + var actualHash = new byte[Sha256.DigestSize]; + Sha256.GenerateSha256Hash(data, actualHash); if (Util.ArraysEqual(expectedHash, actualHash)) return Validity.Valid; diff --git a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs index 63284e69..8ef6f1a4 100644 --- a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs +++ b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs @@ -2,6 +2,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac.FsSystem.NcaUtils @@ -163,7 +164,8 @@ namespace LibHac.FsSystem.NcaUtils // ReSharper disable once ImpureMethodCallOnReadonlyValueField Memory headerData = _header.Slice(offset, NcaHeaderStruct.FsHeaderSize); - byte[] actualHash = CryptoOld.ComputeSha256(headerData.ToArray(), 0, NcaHeaderStruct.FsHeaderSize); + Span actualHash = stackalloc byte[Sha256.DigestSize]; + Sha256.GenerateSha256Hash(headerData.Span, actualHash); if (!Util.SpansEqual(expectedHash, actualHash)) { diff --git a/src/LibHac/FsSystem/PartitionFileSystem.cs b/src/LibHac/FsSystem/PartitionFileSystem.cs index 5cee34c3..c6f65495 100644 --- a/src/LibHac/FsSystem/PartitionFileSystem.cs +++ b/src/LibHac/FsSystem/PartitionFileSystem.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac.FsSystem @@ -164,7 +165,7 @@ namespace LibHac.FsSystem { HashedRegionSize = reader.ReadInt32(); HashedRegionOffset = reader.ReadInt64(); - Hash = reader.ReadBytes(CryptoOld.Sha256DigestSize); + Hash = reader.ReadBytes(Sha256.DigestSize); } else { diff --git a/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs b/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs index 12cf4c82..4c1f4738 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Security.Cryptography; using System.Text; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac.FsSystem @@ -23,7 +23,8 @@ namespace LibHac.FsSystem /// public PartitionFileSystemBuilder(IFileSystem input) { - foreach (DirectoryEntryEx entry in input.EnumerateEntries().OrderBy(x => x.FullPath, StringComparer.Ordinal)) + foreach (DirectoryEntryEx entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File) + .OrderBy(x => x.FullPath, StringComparer.Ordinal)) { input.OpenFile(out IFile file, entry.FullPath, OpenMode.Read).ThrowIfFailure(); @@ -144,22 +145,25 @@ namespace LibHac.FsSystem private void CalculateHashes() { - using (SHA256 sha = SHA256.Create()) + IHash sha = Sha256.CreateSha256Generator(); + + foreach (Entry entry in Entries) { - foreach (Entry entry in Entries) + if (entry.HashLength == 0) entry.HashLength = 0x200; + + var data = new byte[entry.HashLength]; + entry.File.Read(out long bytesRead, entry.HashOffset, data); + + if (bytesRead != entry.HashLength) { - if (entry.HashLength == 0) entry.HashLength = 0x200; - - var data = new byte[entry.HashLength]; - entry.File.Read(out long bytesRead, entry.HashOffset, data); - - if (bytesRead != entry.HashLength) - { - throw new ArgumentOutOfRangeException(); - } - - entry.Hash = sha.ComputeHash(data); + throw new ArgumentOutOfRangeException(); } + + entry.Hash = new byte[Sha256.DigestSize]; + + sha.Initialize(); + sha.Update(data); + sha.GetHash(entry.Hash); } } diff --git a/src/LibHac/FsSystem/Save/Header.cs b/src/LibHac/FsSystem/Save/Header.cs index 47d9f927..25908282 100644 --- a/src/LibHac/FsSystem/Save/Header.cs +++ b/src/LibHac/FsSystem/Save/Header.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac.FsSystem.Save @@ -81,7 +82,10 @@ namespace LibHac.FsSystem.Save MasterHash = storage.Slice(Layout.IvfcMasterHashOffsetA, Layout.IvfcMasterHashSize); - HeaderHashValidity = CryptoOld.CheckMemoryHashTable(Data, Layout.Hash, 0x300, 0x3d00); + Span actualHeaderHash = stackalloc byte[Sha256.DigestSize]; + Sha256.GenerateSha256Hash(Data.AsSpan(0x300, 0x3d00), actualHeaderHash); + + HeaderHashValidity = Util.SpansEqual(Layout.Hash, actualHeaderHash) ? Validity.Valid : Validity.Invalid; SignatureValidity = ValidateSignature(keyset); } diff --git a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs index 851ac3a7..9ac416a7 100644 --- a/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/Save/SaveDataFileSystem.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac.FsSystem.Save @@ -252,7 +253,9 @@ namespace LibHac.FsSystem.Save headerStream.Position = 0x300; headerStream.Read(hashData, 0, hashData.Length); - byte[] hash = CryptoOld.ComputeSha256(hashData, 0, hashData.Length); + var hash = new byte[Sha256.DigestSize]; + Sha256.GenerateSha256Hash(hashData, hash); + headerStream.Position = 0x108; headerStream.Write(hash, 0, hash.Length); diff --git a/src/LibHac/XciHeader.cs b/src/LibHac/XciHeader.cs index 899edac6..6a598674 100644 --- a/src/LibHac/XciHeader.cs +++ b/src/LibHac/XciHeader.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Text; +using LibHac.Crypto; using LibHac.Fs; namespace LibHac @@ -99,8 +100,8 @@ namespace LibHac Array.Reverse(AesCbcIv); RootPartitionOffset = reader.ReadInt64(); RootPartitionHeaderSize = reader.ReadInt64(); - RootPartitionHeaderHash = reader.ReadBytes(CryptoOld.Sha256DigestSize); - InitialDataHash = reader.ReadBytes(CryptoOld.Sha256DigestSize); + RootPartitionHeaderHash = reader.ReadBytes(Sha256.DigestSize); + InitialDataHash = reader.ReadBytes(Sha256.DigestSize); SelSec = reader.ReadInt32(); SelT1Key = reader.ReadInt32(); SelKey = reader.ReadInt32(); @@ -128,10 +129,16 @@ namespace LibHac } } - ImageHash = CryptoOld.ComputeSha256(sigData, 0, sigData.Length); + ImageHash = new byte[Sha256.DigestSize]; + Sha256.GenerateSha256Hash(sigData, ImageHash); reader.BaseStream.Position = RootPartitionOffset; - PartitionFsHeaderValidity = CryptoOld.CheckMemoryHashTable(reader.ReadBytes((int)RootPartitionHeaderSize), RootPartitionHeaderHash, 0, (int)RootPartitionHeaderSize); + byte[] headerBytes = reader.ReadBytes((int) RootPartitionHeaderSize); + + Span actualHeaderHash = stackalloc byte[Sha256.DigestSize]; + Sha256.GenerateSha256Hash(headerBytes, actualHeaderHash); + + PartitionFsHeaderValidity = Util.SpansEqual(RootPartitionHeaderHash, actualHeaderHash) ? Validity.Valid : Validity.Invalid; } } }