diff --git a/src/LibHac/FsSrv/DefaultFsServerObjects.cs b/src/LibHac/FsSrv/DefaultFsServerObjects.cs index a7df3d0c..37779e09 100644 --- a/src/LibHac/FsSrv/DefaultFsServerObjects.cs +++ b/src/LibHac/FsSrv/DefaultFsServerObjects.cs @@ -1,5 +1,7 @@ -using LibHac.Common; +using System; +using LibHac.Common; using LibHac.Common.Keys; +using LibHac.Fs; using LibHac.FsSrv.FsCreator; using LibHac.FsSrv.Storage; using LibHac.FsSystem; @@ -32,14 +34,18 @@ public class DefaultFsServerObjects using SharedRef sharedRootFileSystemCopy = SharedRef.CreateCopy(in sharedRootFileSystem); + var memoryResource = new ArrayPoolMemoryResource(); + IBufferManager bufferManager = null; + IHash256GeneratorFactorySelector ncaHashGeneratorFactorySelector = null; + creators.RomFileSystemCreator = new RomFileSystemCreator(); creators.PartitionFileSystemCreator = new PartitionFileSystemCreator(); - creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet); + creators.StorageOnNcaCreator = new StorageOnNcaCreator(memoryResource, bufferManager, InitializeNcaReader, new NcaCompressionConfiguration(), ncaHashGeneratorFactorySelector); creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator(); creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator(); creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, null, randomGenerator); creators.GameCardStorageCreator = gcStorageCreator; - creators.GameCardFileSystemCreator = new GameCardFileSystemCreator(new ArrayPoolMemoryResource(), gcStorageCreator, fsServer); + creators.GameCardFileSystemCreator = new GameCardFileSystemCreator(memoryResource, gcStorageCreator, fsServer); creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet); creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(ref sharedRootFileSystem.Ref); creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdmmcNew, ref sharedRootFileSystemCopy.Ref); @@ -55,4 +61,11 @@ public class DefaultFsServerObjects StorageDeviceManagerFactory = storageDeviceManagerFactory }; } + + public static Result InitializeNcaReader(ref SharedRef outReader, in SharedRef baseStorage, + in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, + ContentAttributes contentAttributes) + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs b/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs index 79b1cf37..f122ce98 100644 --- a/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs @@ -5,22 +5,16 @@ using LibHac.FsSystem; namespace LibHac.FsSrv.FsCreator; public interface IStorageOnNcaCreator -{ - Result Create(ref SharedRef outStorage, out LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader fsHeader, LibHac.Tools.FsSystem.NcaUtils.Nca nca, int fsIndex, bool isCodeFs); - Result OpenNca(out LibHac.Tools.FsSystem.NcaUtils.Nca nca, IStorage ncaStorage); -} - -public interface IStorageOnNcaCreator17 { Result Create(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader, - ref readonly SharedRef ncaReader, int fsIndex); + ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader, + ref readonly SharedRef ncaReader, int fsIndex); Result CreateWithPatch(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader, - ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader, + ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader, + ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader, int fsIndex); - Result CreateNcaReader(ref SharedRef outReader, ref readonly SharedRef baseStorage, + Result CreateNcaReader(ref SharedRef outReader, ref readonly SharedRef baseStorage, ContentAttributes contentAttributes); } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs b/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs index 90c374a3..aa21e61c 100644 --- a/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs @@ -1,77 +1,84 @@ -using System; using LibHac.Common; -using LibHac.Common.Keys; using LibHac.Fs; -using LibHac.Fs.Fsa; using LibHac.FsSystem; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using NcaFsHeader = LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader; namespace LibHac.FsSrv.FsCreator; +/// +/// Opens the partitions in NCAs as s. +/// +/// Based on nnSdk 17.5.0 (FS 17.0.0) public class StorageOnNcaCreator : IStorageOnNcaCreator { - // ReSharper disable once UnusedMember.Local - private bool IsEnabledProgramVerification { get; set; } - private KeySet KeySet { get; } + private MemoryResource _memoryResource; + private NcaCompressionConfiguration _compressionConfig; + private IBufferManager _bufferManager; + private NcaReaderInitializer _ncaReaderInitializer; + private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector; - public StorageOnNcaCreator(KeySet keySet) + public StorageOnNcaCreator(MemoryResource memoryResource, IBufferManager bufferManager, + NcaReaderInitializer ncaReaderInitializer, in NcaCompressionConfiguration compressionConfig, + IHash256GeneratorFactorySelector hashGeneratorFactorySelector) { - KeySet = keySet; + _memoryResource = memoryResource; + _compressionConfig = compressionConfig; + _bufferManager = bufferManager; + _ncaReaderInitializer = ncaReaderInitializer; + _hashGeneratorFactorySelector = hashGeneratorFactorySelector; } - // todo: Implement NcaReader and other Nca classes - public Result Create(ref SharedRef outStorage, out NcaFsHeader fsHeader, Nca nca, - int fsIndex, bool isCodeFs) + public Result Create(ref SharedRef outStorage, + ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader, + ref readonly SharedRef ncaReader, int fsIndex) { - UnsafeHelpers.SkipParamInit(out fsHeader); + var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector); - Result res = OpenStorage(out IStorage storageTemp, nca, fsIndex); + using var storage = new SharedRef(); + using var storageAccessSplitter = new SharedRef(); + Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, + ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex)); if (res.IsFailure()) return res.Miss(); - if (isCodeFs) - { - using var codeFs = new PartitionFileSystem(); - res = codeFs.Initialize(storageTemp); - if (res.IsFailure()) return res.Miss(); + using var resultConvertStorage = new SharedRef(new RomResultConvertStorage(in storage)); - res = VerifyAcidSignature(codeFs, nca); - if (res.IsFailure()) return res.Miss(); - } - - outStorage.Reset(storageTemp); - fsHeader = nca.GetFsHeader(fsIndex); + outStorage.SetByMove(ref resultConvertStorage.Ref); + outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref); return Result.Success; } - public Result CreateWithPatch(ref SharedRef outStorage, out NcaFsHeader fsHeader, - Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs) + public Result CreateWithPatch(ref SharedRef outStorage, + ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader, + ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader, + int fsIndex) { - throw new NotImplementedException(); - } + var ncaFsDriver = new NcaFileSystemDriver(in originalNcaReader, in currentNcaReader, _memoryResource, + _bufferManager, _hashGeneratorFactorySelector); + + using var storage = new SharedRef(); + using var storageAccessSplitter = new SharedRef(); + Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, + ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex)); + if (res.IsFailure()) return res.Miss(); + + using var resultConvertStorage = new SharedRef(new RomResultConvertStorage(in storage)); + + outStorage.SetByMove(ref resultConvertStorage.Ref); + outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref); - public Result OpenNca(out Nca nca, IStorage ncaStorage) - { - nca = new Nca(KeySet, ncaStorage); return Result.Success; } - public Result VerifyAcidSignature(IFileSystem codeFileSystem, Nca nca) + public Result CreateNcaReader(ref SharedRef outReader, ref readonly SharedRef baseStorage, + ContentAttributes contentAttributes) { - // todo - return Result.Success; - } + using var ncaReader = new SharedRef(); - private Result OpenStorage(out IStorage storage, Nca nca, int fsIndex) - { - UnsafeHelpers.SkipParamInit(out storage); + Result res = RomResultConverter.ConvertRomResult(_ncaReaderInitializer(ref ncaReader.Ref, in baseStorage, + in _compressionConfig, _hashGeneratorFactorySelector, contentAttributes)); + if (res.IsFailure()) return res.Miss(); - if (!nca.SectionExists(fsIndex)) - return ResultFs.PartitionNotFound.Log(); - - storage = nca.OpenStorage(fsIndex, IntegrityCheckLevel.ErrorOnInvalid); + outReader.SetByMove(ref ncaReader.Ref); return Result.Success; } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator17.cs b/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator17.cs deleted file mode 100644 index 9e311964..00000000 --- a/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator17.cs +++ /dev/null @@ -1,84 +0,0 @@ -using LibHac.Common; -using LibHac.Fs; -using LibHac.FsSystem; - -namespace LibHac.FsSrv.FsCreator; - -/// -/// Opens the partitions in NCAs as s. -/// -/// Based on nnSdk 17.5.0 (FS 17.0.0) -public class StorageOnNcaCreator17 : IStorageOnNcaCreator17 -{ - private MemoryResource _memoryResource; - private NcaCompressionConfiguration _compressionConfig; - private IBufferManager _bufferManager; - private NcaReaderInitializer _ncaReaderInitializer; - private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector; - - public StorageOnNcaCreator17(MemoryResource memoryResource, IBufferManager bufferManager, - NcaReaderInitializer ncaReaderInitializer, in NcaCompressionConfiguration compressionConfig, - IHash256GeneratorFactorySelector hashGeneratorFactorySelector) - { - _memoryResource = memoryResource; - _compressionConfig = compressionConfig; - _bufferManager = bufferManager; - _ncaReaderInitializer = ncaReaderInitializer; - _hashGeneratorFactorySelector = hashGeneratorFactorySelector; - } - - public Result Create(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader, - ref readonly SharedRef ncaReader, int fsIndex) - { - var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector); - - using var storage = new SharedRef(); - using var storageAccessSplitter = new SharedRef(); - Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, - ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex)); - if (res.IsFailure()) return res.Miss(); - - using var resultConvertStorage = new SharedRef(new RomResultConvertStorage(in storage)); - - outStorage.SetByMove(ref resultConvertStorage.Ref); - outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref); - - return Result.Success; - } - - public Result CreateWithPatch(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader, - ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader, - int fsIndex) - { - var ncaFsDriver = new NcaFileSystemDriver(in originalNcaReader, in currentNcaReader, _memoryResource, - _bufferManager, _hashGeneratorFactorySelector); - - using var storage = new SharedRef(); - using var storageAccessSplitter = new SharedRef(); - Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, - ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex)); - if (res.IsFailure()) return res.Miss(); - - using var resultConvertStorage = new SharedRef(new RomResultConvertStorage(in storage)); - - outStorage.SetByMove(ref resultConvertStorage.Ref); - outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref); - - return Result.Success; - } - - public Result CreateNcaReader(ref SharedRef outReader, ref readonly SharedRef baseStorage, - ContentAttributes contentAttributes) - { - using var ncaReader = new SharedRef(); - - Result res = RomResultConverter.ConvertRomResult(_ncaReaderInitializer(ref ncaReader.Ref, in baseStorage, - in _compressionConfig, _hashGeneratorFactorySelector, contentAttributes)); - if (res.IsFailure()) return res.Miss(); - - outReader.SetByMove(ref ncaReader.Ref); - return Result.Success; - } -} \ No newline at end of file diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs index fe6dece2..6ab93ce2 100644 --- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs @@ -13,8 +13,6 @@ using LibHac.Spl; using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Util; using static LibHac.Fs.Impl.CommonMountNames; -using NcaFsHeader = LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader; -using NcaHeader = LibHac.FsSystem.NcaHeader; using RightsId = LibHac.Fs.RightsId; using Utility = LibHac.FsSystem.Utility; @@ -272,9 +270,10 @@ public class NcaFileSystemServiceImpl { using SharedRef tempFileSystem = SharedRef.CreateMove(ref subDirFs.Ref); res = _config.EncryptedFsCreator.Create(ref subDirFs.Ref, ref tempFileSystem.Ref, - IEncryptedFileSystemCreator.KeyId.Content, in _encryptionSeed); + IEncryptedFileSystemCreator.KeyId.Content, in _encryptionSeed); if (res.IsFailure()) return res.Miss(); } + outFileSystem.SetByMove(ref subDirFs.Ref); return Result.Success; @@ -355,7 +354,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, ContentStorageSystemMountName, - ContentStorageSystemMountName.Length) == 0) + ContentStorageSystemMountName.Length) == 0) { path = path.Slice(ContentStorageSystemMountName.Length); @@ -366,7 +365,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, ContentStorageUserMountName, - ContentStorageUserMountName.Length) == 0) + ContentStorageUserMountName.Length) == 0) { path = path.Slice(ContentStorageUserMountName.Length); @@ -377,7 +376,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, ContentStorageSdCardMountName, - ContentStorageSdCardMountName.Length) == 0) + ContentStorageSdCardMountName.Length) == 0) { path = path.Slice(ContentStorageSdCardMountName.Length); @@ -388,7 +387,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, BisCalibrationFilePartitionMountName, - BisCalibrationFilePartitionMountName.Length) == 0) + BisCalibrationFilePartitionMountName.Length) == 0) { path = path.Slice(BisCalibrationFilePartitionMountName.Length); @@ -397,7 +396,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, BisSafeModePartitionMountName, - BisSafeModePartitionMountName.Length) == 0) + BisSafeModePartitionMountName.Length) == 0) { path = path.Slice(BisSafeModePartitionMountName.Length); @@ -406,7 +405,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, BisUserPartitionMountName, - BisUserPartitionMountName.Length) == 0) + BisUserPartitionMountName.Length) == 0) { path = path.Slice(BisUserPartitionMountName.Length); @@ -415,7 +414,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, BisSystemPartitionMountName, - BisSystemPartitionMountName.Length) == 0) + BisSystemPartitionMountName.Length) == 0) { path = path.Slice(BisSystemPartitionMountName.Length); @@ -424,7 +423,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, SdCardFileSystemMountName, - SdCardFileSystemMountName.Length) == 0) + SdCardFileSystemMountName.Length) == 0) { path = path.Slice(SdCardFileSystemMountName.Length); @@ -433,7 +432,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, HostRootFileSystemMountName, - HostRootFileSystemMountName.Length) == 0) + HostRootFileSystemMountName.Length) == 0) { path = path.Slice(HostRootFileSystemMountName.Length); @@ -449,7 +448,7 @@ public class NcaFileSystemServiceImpl } else if (StringUtils.Compare(path, RegisteredUpdatePartitionMountName, - RegisteredUpdatePartitionMountName.Length) == 0) + RegisteredUpdatePartitionMountName.Length) == 0) { path = path.Slice(RegisteredUpdatePartitionMountName.Length); @@ -599,34 +598,7 @@ public class NcaFileSystemServiceImpl private Result ParseNca(ref U8Span path, out Nca nca, ref SharedRef baseFileSystem, ulong ncaId) { - UnsafeHelpers.SkipParamInit(out nca); - - // Todo: Create ref-counted storage - var ncaFileStorage = new FileStorageBasedFileSystem(); - - using var pathNca = new Path(); - Result res = pathNca.InitializeWithNormalization(path); - if (res.IsFailure()) return res.Miss(); - - res = ncaFileStorage.Initialize(ref baseFileSystem, in pathNca, OpenMode.Read); - if (res.IsFailure()) return res.Miss(); - - res = _config.StorageOnNcaCreator.OpenNca(out Nca ncaTemp, ncaFileStorage); - if (res.IsFailure()) return res.Miss(); - - if (ncaId == ulong.MaxValue) - { - ulong ncaProgramId = ncaTemp.Header.TitleId; - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (ncaProgramId != ulong.MaxValue && ncaId != ncaProgramId) - { - return ResultFs.InvalidNcaId.Log(); - } - } - - nca = ncaTemp; - return Result.Success; + throw new NotImplementedException(); } private Result ParseContentTypeForDirectory(ref SharedRef outFileSystem, @@ -696,63 +668,7 @@ public class NcaFileSystemServiceImpl private Result OpenStorageByContentType(ref SharedRef outNcaStorage, Nca nca, out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate) { - UnsafeHelpers.SkipParamInit(out fsType); - - NcaHeader.ContentType contentType = (NcaHeader.ContentType)nca.Header.ContentType; - - switch (fsProxyType) - { - case FileSystemProxyType.Code: - case FileSystemProxyType.Rom: - case FileSystemProxyType.Logo: - case FileSystemProxyType.RegisteredUpdate: - if (contentType != NcaHeader.ContentType.Program) - return ResultFs.PreconditionViolation.Log(); - - break; - - case FileSystemProxyType.Control: - if (contentType != NcaHeader.ContentType.Control) - return ResultFs.PreconditionViolation.Log(); - - break; - case FileSystemProxyType.Manual: - if (contentType != NcaHeader.ContentType.Manual) - return ResultFs.PreconditionViolation.Log(); - - break; - case FileSystemProxyType.Meta: - if (contentType != NcaHeader.ContentType.Meta) - return ResultFs.PreconditionViolation.Log(); - - break; - case FileSystemProxyType.Data: - if (contentType != NcaHeader.ContentType.Data && contentType != NcaHeader.ContentType.PublicData) - return ResultFs.PreconditionViolation.Log(); - - if (contentType == NcaHeader.ContentType.Data && !canMountSystemDataPrivate) - return ResultFs.PermissionDenied.Log(); - - break; - default: - return ResultFs.InvalidArgument.Log(); - } - - if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard) - return ResultFs.PermissionDenied.Log(); - - Result res = SetExternalKeyForRightsId(nca); - if (res.IsFailure()) return res.Miss(); - - res = GetPartitionIndex(out int sectionIndex, fsProxyType); - if (res.IsFailure()) return res.Miss(); - - res = _config.StorageOnNcaCreator.Create(ref outNcaStorage, out NcaFsHeader fsHeader, nca, - sectionIndex, fsProxyType == FileSystemProxyType.Code); - if (res.IsFailure()) return res.Miss(); - - fsType = fsHeader.FormatType; - return Result.Success; + throw new NotImplementedException(); } public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed) diff --git a/src/LibHac/FsSystem/AesXtsStorageExternal.cs b/src/LibHac/FsSystem/AesXtsStorageExternal.cs index 106ce249..ce549c62 100644 --- a/src/LibHac/FsSystem/AesXtsStorageExternal.cs +++ b/src/LibHac/FsSystem/AesXtsStorageExternal.cs @@ -9,6 +9,8 @@ using LibHac.Util; namespace LibHac.FsSystem; +public delegate Result CryptAesXtsFunction(Span dest, ReadOnlySpan key1, ReadOnlySpan key2, ReadOnlySpan iv, ReadOnlySpan source); + /// /// Reads and writes to an that's encrypted with AES-XTS-128. /// All encryption or decryption will be done externally via a provided function. diff --git a/src/LibHac/FsSystem/NcaFileSystemDriver.cs b/src/LibHac/FsSystem/NcaFileSystemDriver.cs index d31d651c..877ad27d 100644 --- a/src/LibHac/FsSystem/NcaFileSystemDriver.cs +++ b/src/LibHac/FsSystem/NcaFileSystemDriver.cs @@ -1,11 +1,8 @@ -// ReSharper disable UnusedMember.Local -using System; +using System; using LibHac.Common; -using LibHac.Common.FixedArrays; using LibHac.Crypto; using LibHac.Diag; using LibHac.Fs; -using LibHac.FsSrv; namespace LibHac.FsSystem; @@ -30,20 +27,6 @@ public struct NcaCryptoConfiguration public const int KeyGenerationMax = 32; public const int KeyAreaEncryptionKeyCount = KeyAreaEncryptionKeyIndexCount * KeyGenerationMax; - - public Array2> Header1SignKeyModuli; - public Array3 Header1SignKeyPublicExponent; - public Array3> KeyAreaEncryptionKeySources; - public Array16 HeaderEncryptionKeySource; - public Array2> HeaderEncryptedEncryptionKeys; - public GenerateKeyFunction GenerateKey; - public CryptAesXtsFunction EncryptAesXtsForExternalKey; - public CryptAesXtsFunction DecryptAesXtsForExternalKey; - public DecryptAesCtrFunction DecryptAesCtr; - public DecryptAesCtrFunction DecryptAesCtrForExternalKey; - public VerifySign1Function VerifySign1; - public bool IsDev; - public bool IsAvailableSwKey; } public struct NcaCompressionConfiguration @@ -88,12 +71,12 @@ public enum KeyType file static class Anonymous { - public static long GetFsOffset(NcaReader17 reader, int index) + public static long GetFsOffset(NcaReader reader, int index) { return (long)reader.GetFsOffset(index); } - public static long GetFsEndOffset(NcaReader17 reader, int index) + public static long GetFsEndOffset(NcaReader reader, int index) { return (long)reader.GetFsEndOffset(index); } @@ -102,12 +85,12 @@ file static class Anonymous file class SharedNcaBodyStorage : IStorage { private SharedRef _storage; - private SharedRef _ncaReader; + private SharedRef _ncaReader; - public SharedNcaBodyStorage(in SharedRef baseStorage, in SharedRef ncaReader) + public SharedNcaBodyStorage(in SharedRef baseStorage, in SharedRef ncaReader) { _storage = SharedRef.CreateCopy(in baseStorage); - _ncaReader = SharedRef.CreateCopy(in ncaReader); + _ncaReader = SharedRef.CreateCopy(in ncaReader); } public override void Dispose() @@ -202,14 +185,14 @@ public class NcaFileSystemDriver : IDisposable None = 1 } - public NcaFileSystemDriver(ref readonly SharedRef ncaReader, MemoryResource allocator, + public NcaFileSystemDriver(ref readonly SharedRef ncaReader, MemoryResource allocator, IBufferManager bufferManager, IHash256GeneratorFactorySelector hashGeneratorFactorySelector) { throw new NotImplementedException(); } - public NcaFileSystemDriver(ref readonly SharedRef originalNcaReader, - ref readonly SharedRef currentNcaReader, MemoryResource allocator, IBufferManager bufferManager, + public NcaFileSystemDriver(ref readonly SharedRef originalNcaReader, + ref readonly SharedRef currentNcaReader, MemoryResource allocator, IBufferManager bufferManager, IHash256GeneratorFactorySelector hashGeneratorFactorySelector) { throw new NotImplementedException(); @@ -221,20 +204,20 @@ public class NcaFileSystemDriver : IDisposable } public Result OpenStorage(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader, + ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader, int fsIndex) { throw new NotImplementedException(); } - private Result OpenStorageImpl(ref SharedRef outStorage, out NcaFsHeaderReader17 outHeaderReader, + private Result OpenStorageImpl(ref SharedRef outStorage, out NcaFsHeaderReader outHeaderReader, int fsIndex, ref StorageContext storageContext) { throw new NotImplementedException(); } private Result OpenIndirectableStorageAsOriginal(ref SharedRef outStorage, - in NcaFsHeaderReader17 headerReader, ref StorageContext storageContext) + in NcaFsHeaderReader headerReader, ref StorageContext storageContext) { throw new NotImplementedException(); } @@ -375,7 +358,7 @@ public class NcaFileSystemDriver : IDisposable throw new NotImplementedException(); } - private Result CreateRegionSwitchStorage(ref SharedRef outStorage, in NcaFsHeaderReader17 headerReader, + private Result CreateRegionSwitchStorage(ref SharedRef outStorage, in NcaFsHeaderReader headerReader, ref readonly SharedRef insideRegionStorage, ref readonly SharedRef outsideRegionStorage) { throw new NotImplementedException(); diff --git a/src/LibHac/FsSystem/NcaReader.cs b/src/LibHac/FsSystem/NcaReader.cs index 077df87d..26b1c73b 100644 --- a/src/LibHac/FsSystem/NcaReader.cs +++ b/src/LibHac/FsSystem/NcaReader.cs @@ -1,201 +1,53 @@ -using System; +using System; using System.Runtime.CompilerServices; using LibHac.Common; -using LibHac.Common.FixedArrays; using LibHac.Crypto; using LibHac.Diag; using LibHac.Fs; +using LibHac.Spl; namespace LibHac.FsSystem; -public delegate void GenerateKeyFunction(Span destKey, ReadOnlySpan sourceKey, int keyType); -public delegate Result DecryptAesCtrFunction(Span dest, int keyIndex, int keyGeneration, ReadOnlySpan encryptedKey, ReadOnlySpan iv, ReadOnlySpan source); -public delegate Result CryptAesXtsFunction(Span dest, ReadOnlySpan key1, ReadOnlySpan key2, ReadOnlySpan iv, ReadOnlySpan source); -public delegate bool VerifySign1Function(ReadOnlySpan signature, ReadOnlySpan data, bool isProd, byte generation); +public delegate Result NcaReaderInitializer(ref SharedRef outReader, in SharedRef baseStorage, + in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, + ContentAttributes contentAttributes); /// -/// Handles reading information from an NCA file's header. +/// Handles reading information from an NCA's header. /// -/// Based on nnSdk 14.3.0 (FS 14.1.0) +/// Based on nnSdk 17.5.0 (FS 17.0.0) public class NcaReader : IDisposable { - private const uint SdkAddonVersionMin = 0xB0000; - - private NcaHeader _header; - private Array5> _decryptionKeys; + private RuntimeNcaHeader _header; private SharedRef _bodyStorage; - private UniqueRef _headerStorage; - private Array16 _externalDataDecryptionKey; - private DecryptAesCtrFunction _decryptAesCtr; - private DecryptAesCtrFunction _decryptAesCtrForExternalKey; - private bool _isSoftwareAesPrioritized; - private bool _isAvailableSwKey; - private NcaHeader.EncryptionType _headerEncryptionType; + private SharedRef _headerStorage; + private SharedRef _aesCtrDecryptor; private GetDecompressorFunction _getDecompressorFunc; private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector; - public void Dispose() - { - _headerStorage.Destroy(); - _bodyStorage.Destroy(); - } - - public Result Initialize(ref SharedRef baseStorage, in NcaCryptoConfiguration cryptoConfig, + public NcaReader(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef notVerifiedHeaderStorage, + ref readonly SharedRef bodyStorage, ref readonly SharedRef aesCtrDecryptor, in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector) { - Assert.SdkRequiresNotNull(in baseStorage); + Assert.SdkRequiresNotNull(in notVerifiedHeaderStorage); + Assert.SdkRequiresNotNull(in bodyStorage); Assert.SdkRequiresNotNull(hashGeneratorFactorySelector); - Assert.SdkRequiresNull(in _bodyStorage); - if (cryptoConfig.VerifySign1 is null) - return ResultFs.InvalidArgument.Log(); + _header = runtimeNcaHeader; - using var headerStorage = new UniqueRef(); + _headerStorage = SharedRef.CreateCopy(in notVerifiedHeaderStorage); + _bodyStorage = SharedRef.CreateCopy(in bodyStorage); + _aesCtrDecryptor = SharedRef.CreateCopy(in aesCtrDecryptor); - if (cryptoConfig.IsAvailableSwKey) - { - if (cryptoConfig.GenerateKey is null) - return ResultFs.InvalidArgument.Log(); - - ReadOnlySpan headerKeyTypes = stackalloc int[NcaCryptoConfiguration.HeaderEncryptionKeyCount] - { (int)KeyType.NcaHeaderKey1, (int)KeyType.NcaHeaderKey2 }; - - // Generate the keys for decrypting the NCA header. - Unsafe.SkipInit(out Array2> commonDecryptionKeys); - for (int i = 0; i < NcaCryptoConfiguration.HeaderEncryptionKeyCount; i++) - { - cryptoConfig.GenerateKey(commonDecryptionKeys[i], cryptoConfig.HeaderEncryptedEncryptionKeys[i], - headerKeyTypes[i]); - } - - // Create an XTS storage to read the encrypted header. - Array16 headerIv = default; - headerStorage.Reset(new AesXtsStorage(baseStorage.Get, commonDecryptionKeys[0], commonDecryptionKeys[1], - headerIv, NcaHeader.XtsBlockSize)); - } - else - { - // Software key isn't available, so we need to be able to decrypt externally. - if (cryptoConfig.DecryptAesXtsForExternalKey is null) - return ResultFs.InvalidArgument.Log(); - - // Create the header storage. - Array16 headerIv = default; - headerStorage.Reset(new AesXtsStorageExternal(baseStorage.Get, ReadOnlySpan.Empty, - ReadOnlySpan.Empty, headerIv, (uint)NcaHeader.XtsBlockSize, cryptoConfig.EncryptAesXtsForExternalKey, - cryptoConfig.DecryptAesXtsForExternalKey)); - } - - if (!headerStorage.HasValue) - return ResultFs.AllocationMemoryFailedInNcaReaderA.Log(); - - // Read the decrypted header. - Result res = headerStorage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); - if (res.IsFailure()) return res.Miss(); - - // Check if the NCA magic value is correct. - Result signatureResult = CheckSignature(in _header); - if (signatureResult.IsFailure()) - { - // If the magic value is not correct the header might not be encrypted. - if (cryptoConfig.IsDev) - { - // Read the header without decrypting it and check the magic value again. - res = baseStorage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); - if (res.IsFailure()) return res.Miss(); - - res = CheckSignature(in _header); - if (res.IsFailure()) - return signatureResult.Miss(); - - // We have a plaintext header. Get an IStorage of just the header. - res = baseStorage.Get.GetSize(out long baseStorageSize); - if (res.IsFailure()) return res.Miss(); - - headerStorage.Reset(new SubStorage(in baseStorage, 0, baseStorageSize)); - - if (!headerStorage.HasValue) - return ResultFs.AllocationMemoryFailedInNcaReaderA.Log(); - - _headerEncryptionType = NcaHeader.EncryptionType.None; - } - else - { - return signatureResult.Miss(); - } - } - - // Validate the fixed key signature. - if (_header.Header1SignatureKeyGeneration > NcaCryptoConfiguration.Header1SignatureKeyGenerationMax) - return ResultFs.InvalidNcaHeader1SignatureKeyGeneration.Log(); - - int signMessageOffset = NcaHeader.HeaderSignSize * NcaHeader.HeaderSignCount; - int signMessageSize = NcaHeader.Size - signMessageOffset; - ReadOnlySpan signature = _header.Signature1; - ReadOnlySpan message = SpanHelpers.AsReadOnlyByteSpan(in _header).Slice(signMessageOffset, signMessageSize); - - if (!cryptoConfig.VerifySign1(signature, message, !cryptoConfig.IsDev, _header.Header1SignatureKeyGeneration)) - return ResultFs.NcaHeaderSignature1VerificationFailed.Log(); - - // Validate the sdk version. - if (_header.SdkAddonVersion < SdkAddonVersionMin) - return ResultFs.UnsupportedSdkVersion.Log(); - - // Validate the key index. - if (_header.KeyAreaEncryptionKeyIndex >= NcaCryptoConfiguration.KeyAreaEncryptionKeyIndexCount && - _header.KeyAreaEncryptionKeyIndex != NcaCryptoConfiguration.KeyAreaEncryptionKeyIndexZeroKey) - { - return ResultFs.InvalidNcaKeyIndex.Log(); - } - - _hashGeneratorFactorySelector = hashGeneratorFactorySelector; - - // Get keys from the key area if the NCA doesn't have a rights ID. - Array16 zeroRightsId = default; - if (CryptoUtil.IsSameBytes(zeroRightsId, _header.RightsId, NcaHeader.RightsIdSize)) - { - // If we don't have a rights ID we need to generate decryption keys if software keys are available. - if (cryptoConfig.IsAvailableSwKey) - { - int keyTypeValue = NcaKeyFunctions.GetKeyTypeValue(_header.KeyAreaEncryptionKeyIndex, _header.GetProperKeyGeneration()); - ReadOnlySpan encryptedKeyCtr = _header.EncryptedKeys[..].Slice((int)NcaHeader.DecryptionKey.AesCtr * Aes.KeySize128, Aes.KeySize128); - - cryptoConfig.GenerateKey(_decryptionKeys[(int)NcaHeader.DecryptionKey.AesCtr], encryptedKeyCtr, keyTypeValue); - } - - // Copy the plaintext hardware key. - ReadOnlySpan keyCtrHw = _header.EncryptedKeys[..].Slice((int)NcaHeader.DecryptionKey.AesCtrHw * Aes.KeySize128, Aes.KeySize128); - keyCtrHw.CopyTo(_decryptionKeys[(int)NcaHeader.DecryptionKey.AesCtrHw]); - } - - // Clear the external decryption key. - _externalDataDecryptionKey[..].Clear(); - - // Copy the configuration to the NcaReader. - _isAvailableSwKey = cryptoConfig.IsAvailableSwKey; - _decryptAesCtr = cryptoConfig.DecryptAesCtr; - _decryptAesCtrForExternalKey = cryptoConfig.DecryptAesCtrForExternalKey; _getDecompressorFunc = compressionConfig.GetDecompressorFunc; + _hashGeneratorFactorySelector = hashGeneratorFactorySelector; + } - _bodyStorage.SetByMove(ref baseStorage); - _headerStorage.Set(ref headerStorage.Ref); - - return Result.Success; - - static Result CheckSignature(in NcaHeader header) - { - if (header.Magic == NcaHeader.Magic0 || - header.Magic == NcaHeader.Magic1 || - header.Magic == NcaHeader.Magic2) - { - return ResultFs.UnsupportedSdkVersion.Log(); - } - - if (header.Magic != NcaHeader.CurrentMagic) - return ResultFs.InvalidNcaSignature.Log(); - - return Result.Success; - } + public void Dispose() + { + _bodyStorage.Destroy(); + _headerStorage.Destroy(); + _aesCtrDecryptor.Destroy(); } public Result ReadHeader(out NcaFsHeader outHeader, int index) @@ -204,29 +56,23 @@ public class NcaReader : IDisposable Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - long offset = Unsafe.SizeOf() + Unsafe.SizeOf() * (long)index; - return _headerStorage.Get.Read(offset, SpanHelpers.AsByteSpan(ref outHeader)); + long offset = _header.FsHeadersOffset + Unsafe.SizeOf() * (long)index; + return _headerStorage.Get.Read(offset, SpanHelpers.AsByteSpan(ref outHeader)).Ret(); } - public void GetHeaderSign2(Span outBuffer) + public Result GetHeaderSign2(Span outBuffer) { - Assert.SdkRequiresEqual(NcaHeader.HeaderSignSize, outBuffer.Length); + Assert.SdkRequiresGreaterEqual((uint)outBuffer.Length, _header.Header2SignInfo.Size); - _header.Signature2[..].CopyTo(outBuffer); + return _headerStorage.Get + .Read(_header.Header2SignInfo.Size, outBuffer.Slice(0, (int)_header.Header2SignInfo.Size)).Ret(); } public void GetHeaderSign2TargetHash(Span outBuffer) { - Assert.SdkRequiresNotNull(_hashGeneratorFactorySelector); - Assert.SdkRequiresEqual(IHash256Generator.HashSize, outBuffer.Length); + Assert.SdkRequiresEqual(outBuffer.Length, Unsafe.SizeOf()); - int signTargetOffset = NcaHeader.HeaderSignSize * NcaHeader.HeaderSignCount; - int signTargetSize = NcaHeader.Size - signTargetOffset; - ReadOnlySpan signTarget = - SpanHelpers.AsReadOnlyByteSpan(in _header).Slice(signTargetOffset, signTargetSize); - - IHash256GeneratorFactory factory = _hashGeneratorFactorySelector.GetFactory(HashAlgorithmType.Sha2); - factory.GenerateHash(outBuffer, signTarget); + _header.Header2SignInfo.Hash.Value[..].CopyTo(outBuffer); } public SharedRef GetSharedBodyStorage() @@ -236,60 +82,26 @@ public class NcaReader : IDisposable return SharedRef.CreateCopy(in _bodyStorage); } - public uint GetSignature() - { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.Magic; - } - public NcaHeader.DistributionType GetDistributionType() { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.DistributionTypeValue; + return _header.DistributionType; } public NcaHeader.ContentType GetContentType() { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.ContentTypeValue; + return _header.ContentType; } public byte GetKeyGeneration() { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.GetProperKeyGeneration(); - } - - public byte GetKeyIndex() - { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.KeyAreaEncryptionKeyIndex; - } - - public ulong GetContentSize() - { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.ContentSize; + return _header.KeyGeneration; } public ulong GetProgramId() { - Assert.SdkRequiresNotNull(_bodyStorage); return _header.ProgramId; } - public uint GetContentIndex() - { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.ContentIndex; - } - - public uint GetSdkAddonVersion() - { - Assert.SdkRequiresNotNull(_bodyStorage); - return _header.SdkAddonVersion; - } - public void GetRightsId(Span outBuffer) { Assert.SdkRequiresGreaterEqual(outBuffer.Length, NcaHeader.RightsIdSize); @@ -300,57 +112,30 @@ public class NcaReader : IDisposable public bool HasFsInfo(int index) { Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - return _header.FsInfos[index].StartSector != 0 || _header.FsInfos[index].EndSector != 0; } - public int GetFsCount() - { - Assert.SdkRequiresNotNull(_bodyStorage); - - for (int i = 0; i < NcaHeader.FsCountMax; i++) - { - if (!HasFsInfo(i)) - { - return i; - } - } - - return NcaHeader.FsCountMax; - } - - public NcaHeader.EncryptionType GetEncryptionType() - { - return _headerEncryptionType; - } - - public ref readonly Hash GetFsHeaderHash(int index) - { - Assert.SdkRequiresNotNull(_bodyStorage); - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - return ref _header.FsHeaderHashes[index]; - } - public void GetFsHeaderHash(out Hash outHash, int index) { - Assert.SdkRequiresNotNull(_bodyStorage); Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - outHash = _header.FsHeaderHashes[index]; + outHash = _header.FsInfos[index].Hash; } public void GetFsInfo(out NcaHeader.FsInfo outFsInfo, int index) { - Assert.SdkRequiresNotNull(_bodyStorage); Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - outFsInfo = _header.FsInfos[index]; + outFsInfo = new NcaHeader.FsInfo + { + StartSector = _header.FsInfos[index].StartSector, + EndSector = _header.FsInfos[index].EndSector, + HashSectors = _header.FsInfos[index].HashSectors, + Reserved = 0 + }; } public ulong GetFsOffset(int index) { - Assert.SdkRequiresNotNull(_bodyStorage); Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); return NcaHeader.SectorToByte(_header.FsInfos[index].StartSector); @@ -358,7 +143,6 @@ public class NcaReader : IDisposable public ulong GetFsEndOffset(int index) { - Assert.SdkRequiresNotNull(_bodyStorage); Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector); @@ -366,102 +150,35 @@ public class NcaReader : IDisposable public ulong GetFsSize(int index) { - Assert.SdkRequiresNotNull(_bodyStorage); Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector - _header.FsInfos[index].StartSector); } - public void GetEncryptedKey(Span outBuffer) - { - Assert.SdkRequiresNotNull(_bodyStorage); - Assert.SdkRequiresGreaterEqual(outBuffer.Length, NcaHeader.EncryptedKeyAreaSize); - - _header.EncryptedKeys[..].CopyTo(outBuffer); - } - - public ReadOnlySpan GetDecryptionKey(int index) - { - Assert.SdkRequiresNotNull(_bodyStorage); - Assert.SdkRequiresInRange(index, 0, (int)NcaHeader.DecryptionKey.Count); - - return _decryptionKeys[index]; - } - - public bool HasValidInternalKey() - { - Array16 zeroKey = default; - - for (int i = 0; i < (int)NcaHeader.DecryptionKey.Count; i++) - { - if (!CryptoUtil.IsSameBytes(zeroKey, - _header.EncryptedKeys[..].Slice(i * Aes.KeySize128, Aes.KeySize128), Aes.KeySize128)) - { - return true; - } - } - - return false; - } - - public bool HasInternalDecryptionKeyForAesHw() - { - Array16 zeroKey = default; - return !CryptoUtil.IsSameBytes(zeroKey, GetDecryptionKey((int)NcaHeader.DecryptionKey.AesCtrHw), - zeroKey.Length); - } - - public bool IsSwAesPrioritized() - { - return _isSoftwareAesPrioritized; - } - public void PrioritizeSwAes() { - _isSoftwareAesPrioritized = true; + if (_aesCtrDecryptor.HasValue) + { + _aesCtrDecryptor.Get.PrioritizeSw(); + } } - public bool IsAvailableSwKey() + public void SetExternalDecryptionKey(in AccessKey keySource) { - return _isAvailableSwKey; + if (_aesCtrDecryptor.HasValue) + { + _aesCtrDecryptor.Get.SetExternalKeySource(in keySource); + } } - public void SetExternalDecryptionKey(ReadOnlySpan key) + public RuntimeNcaHeader GetHeader() { - Assert.SdkRequiresEqual(_externalDataDecryptionKey[..].Length, key.Length); - - key.CopyTo(_externalDataDecryptionKey); + return _header; } - public ReadOnlySpan GetExternalDecryptionKey() + public SharedRef GetDecryptor() { - return _externalDataDecryptionKey; - } - - public bool HasExternalDecryptionKey() - { - Array16 zeroKey = default; - return !CryptoUtil.IsSameBytes(zeroKey, GetExternalDecryptionKey(), zeroKey.Length); - } - - public void GetRawData(Span outBuffer) - { - Assert.SdkRequires(_bodyStorage.HasValue); - Assert.SdkRequiresLessEqual(Unsafe.SizeOf(), outBuffer.Length); - - SpanHelpers.AsReadOnlyByteSpan(in _header).CopyTo(outBuffer); - } - - public DecryptAesCtrFunction GetExternalDecryptAesCtrFunction() - { - Assert.SdkRequiresNotNull(_decryptAesCtr); - return _decryptAesCtr; - } - - public DecryptAesCtrFunction GetExternalDecryptAesCtrFunctionForExternalKey() - { - Assert.SdkRequiresNotNull(_decryptAesCtrForExternalKey); - return _decryptAesCtrForExternalKey; + return SharedRef.CreateCopy(in _aesCtrDecryptor); } public GetDecompressorFunction GetDecompressor() @@ -475,12 +192,44 @@ public class NcaReader : IDisposable Assert.SdkRequiresNotNull(_hashGeneratorFactorySelector); return _hashGeneratorFactorySelector; } + + public Result Verify() + { + Assert.SdkRequiresNotNull(_bodyStorage); + + for (int fsIndex = 0; fsIndex < NcaHeader.FsCountMax; fsIndex++) + { + var reader = new NcaFsHeaderReader(); + if (HasFsInfo(fsIndex)) + { + Result res = reader.Initialize(this, fsIndex); + if (res.IsFailure()) return res.Miss(); + + res = reader.Verify(_header.ContentType); + if (res.IsFailure()) return res.Miss(); + } + else + { + Result res = ReadHeader(out NcaFsHeader header, fsIndex); + if (res.IsFailure()) return res.Miss(); + + NcaFsHeader zero = default; + if (!CryptoUtil.IsSameBytes(SpanHelpers.AsReadOnlyByteSpan(in header), + SpanHelpers.AsReadOnlyByteSpan(in zero), Unsafe.SizeOf())) + { + return ResultFs.InvalidNcaFsHeader.Log(); + } + } + } + + return Result.Success; + } } /// /// Handles reading information from the of a file system inside an NCA file. /// -/// Based on nnSdk 14.3.0 (FS 14.1.0) +/// Based on nnSdk 17.5.0 (FS 17.0.0) public class NcaFsHeaderReader { private NcaFsHeader _header; @@ -507,7 +256,9 @@ public class NcaFsHeaderReader IHash256GeneratorFactory generator = reader.GetHashGeneratorFactorySelector().GetFactory(HashAlgorithmType.Sha2); generator.GenerateHash(hash.Value, SpanHelpers.AsReadOnlyByteSpan(in _header)); - if (!CryptoUtil.IsSameBytes(reader.GetFsHeaderHash(index).Value, hash.Value, Unsafe.SizeOf())) + reader.GetFsHeaderHash(out Hash fsHeaderHash, index); + + if (!CryptoUtil.IsSameBytes(fsHeaderHash.Value, hash.Value, Unsafe.SizeOf())) { return ResultFs.NcaFsHeaderHashVerificationFailed.Log(); } @@ -647,4 +398,61 @@ public class NcaFsHeaderReader SpanHelpers.AsReadOnlyByteSpan(in _header).CopyTo(outBuffer); } + + public Result Verify(NcaHeader.ContentType contentType) + { + Assert.SdkRequires(IsInitialized()); + Assert.SdkRequiresWithinMinMax((int)contentType, (int)NcaHeader.ContentType.Program, (int)NcaHeader.ContentType.PublicData); + + Result res = _header.Verify(); + if (res.IsFailure()) return res.Miss(); + + const uint programSecureValue = 1; + const uint dataSecureValue = 2; + const uint htmlDocumentSecureValue = 4; + const uint legalInformationSecureValue = 5; + + // Mask out the program index part of the secure value + uint secureValue = _header.AesCtrUpperIv.SecureValue & 0xFFFFFF; + + if (GetEncryptionType() == NcaFsHeader.EncryptionType.None) + { + if (secureValue != 0) + return ResultFs.InvalidNcaFsHeader.Log(); + + return Result.Success; + } + + switch (contentType) + { + case NcaHeader.ContentType.Program: + switch (_fsIndex) + { + case 0: + if (secureValue != programSecureValue) + return ResultFs.InvalidNcaFsHeader.Log(); + break; + case 1: + if (secureValue != dataSecureValue) + return ResultFs.InvalidNcaFsHeader.Log(); + break; + default: + if (secureValue != 0) + return ResultFs.InvalidNcaFsHeader.Log(); + break; + } + + break; + case NcaHeader.ContentType.Manual: + if (secureValue != htmlDocumentSecureValue && secureValue != legalInformationSecureValue) + return ResultFs.InvalidNcaFsHeader.Log(); + break; + default: + if (secureValue != 0) + return ResultFs.InvalidNcaFsHeader.Log(); + break; + } + + return Result.Success; + } } \ No newline at end of file diff --git a/src/LibHac/FsSystem/NcaReader17.cs b/src/LibHac/FsSystem/NcaReader17.cs deleted file mode 100644 index b9deb1e7..00000000 --- a/src/LibHac/FsSystem/NcaReader17.cs +++ /dev/null @@ -1,458 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using LibHac.Common; -using LibHac.Crypto; -using LibHac.Diag; -using LibHac.Fs; -using LibHac.Spl; - -namespace LibHac.FsSystem; - -public delegate Result NcaReaderInitializer(ref SharedRef outReader, in SharedRef baseStorage, - in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, - ContentAttributes contentAttributes); - -/// -/// Handles reading information from an NCA's header. -/// -/// Based on nnSdk 17.5.0 (FS 17.0.0) -public class NcaReader17 : IDisposable -{ - private RuntimeNcaHeader _header; - private SharedRef _bodyStorage; - private SharedRef _headerStorage; - private SharedRef _aesCtrDecryptor; - private GetDecompressorFunction _getDecompressorFunc; - private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector; - - public NcaReader17(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef notVerifiedHeaderStorage, - ref readonly SharedRef bodyStorage, ref readonly SharedRef aesCtrDecryptor, - in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector) - { - Assert.SdkRequiresNotNull(in notVerifiedHeaderStorage); - Assert.SdkRequiresNotNull(in bodyStorage); - Assert.SdkRequiresNotNull(hashGeneratorFactorySelector); - - _header = runtimeNcaHeader; - - _headerStorage = SharedRef.CreateCopy(in notVerifiedHeaderStorage); - _bodyStorage = SharedRef.CreateCopy(in bodyStorage); - _aesCtrDecryptor = SharedRef.CreateCopy(in aesCtrDecryptor); - - _getDecompressorFunc = compressionConfig.GetDecompressorFunc; - _hashGeneratorFactorySelector = hashGeneratorFactorySelector; - } - - public void Dispose() - { - _bodyStorage.Destroy(); - _headerStorage.Destroy(); - _aesCtrDecryptor.Destroy(); - } - - public Result ReadHeader(out NcaFsHeader outHeader, int index) - { - UnsafeHelpers.SkipParamInit(out outHeader); - - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - long offset = _header.FsHeadersOffset + Unsafe.SizeOf() * (long)index; - return _headerStorage.Get.Read(offset, SpanHelpers.AsByteSpan(ref outHeader)).Ret(); - } - - public Result GetHeaderSign2(Span outBuffer) - { - Assert.SdkRequiresGreaterEqual((uint)outBuffer.Length, _header.Header2SignInfo.Size); - - return _headerStorage.Get - .Read(_header.Header2SignInfo.Size, outBuffer.Slice(0, (int)_header.Header2SignInfo.Size)).Ret(); - } - - public void GetHeaderSign2TargetHash(Span outBuffer) - { - Assert.SdkRequiresEqual(outBuffer.Length, Unsafe.SizeOf()); - - _header.Header2SignInfo.Hash.Value[..].CopyTo(outBuffer); - } - - public SharedRef GetSharedBodyStorage() - { - Assert.SdkRequiresNotNull(_bodyStorage); - - return SharedRef.CreateCopy(in _bodyStorage); - } - - public NcaHeader.DistributionType GetDistributionType() - { - return _header.DistributionType; - } - - public NcaHeader.ContentType GetContentType() - { - return _header.ContentType; - } - - public byte GetKeyGeneration() - { - return _header.KeyGeneration; - } - - public ulong GetProgramId() - { - return _header.ProgramId; - } - - public void GetRightsId(Span outBuffer) - { - Assert.SdkRequiresGreaterEqual(outBuffer.Length, NcaHeader.RightsIdSize); - - _header.RightsId[..].CopyTo(outBuffer); - } - - public bool HasFsInfo(int index) - { - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - return _header.FsInfos[index].StartSector != 0 || _header.FsInfos[index].EndSector != 0; - } - - public void GetFsHeaderHash(out Hash outHash, int index) - { - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - outHash = _header.FsInfos[index].Hash; - } - - public void GetFsInfo(out NcaHeader.FsInfo outFsInfo, int index) - { - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - outFsInfo = new NcaHeader.FsInfo - { - StartSector = _header.FsInfos[index].StartSector, - EndSector = _header.FsInfos[index].EndSector, - HashSectors = _header.FsInfos[index].HashSectors, - Reserved = 0 - }; - } - - public ulong GetFsOffset(int index) - { - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - return NcaHeader.SectorToByte(_header.FsInfos[index].StartSector); - } - - public ulong GetFsEndOffset(int index) - { - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector); - } - - public ulong GetFsSize(int index) - { - Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax); - - return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector - _header.FsInfos[index].StartSector); - } - - public void PrioritizeSwAes() - { - if (_aesCtrDecryptor.HasValue) - { - _aesCtrDecryptor.Get.PrioritizeSw(); - } - } - - public void SetExternalDecryptionKey(in AccessKey keySource) - { - if (_aesCtrDecryptor.HasValue) - { - _aesCtrDecryptor.Get.SetExternalKeySource(in keySource); - } - } - - public RuntimeNcaHeader GetHeader() - { - return _header; - } - - public SharedRef GetDecryptor() - { - return SharedRef.CreateCopy(in _aesCtrDecryptor); - } - - public GetDecompressorFunction GetDecompressor() - { - Assert.SdkRequiresNotNull(_getDecompressorFunc); - return _getDecompressorFunc; - } - - public IHash256GeneratorFactorySelector GetHashGeneratorFactorySelector() - { - Assert.SdkRequiresNotNull(_hashGeneratorFactorySelector); - return _hashGeneratorFactorySelector; - } - - public Result Verify() - { - Assert.SdkRequiresNotNull(_bodyStorage); - - for (int fsIndex = 0; fsIndex < NcaHeader.FsCountMax; fsIndex++) - { - var reader = new NcaFsHeaderReader17(); - if (HasFsInfo(fsIndex)) - { - Result res = reader.Initialize(this, fsIndex); - if (res.IsFailure()) return res.Miss(); - - res = reader.Verify(_header.ContentType); - if (res.IsFailure()) return res.Miss(); - } - else - { - Result res = ReadHeader(out NcaFsHeader header, fsIndex); - if (res.IsFailure()) return res.Miss(); - - NcaFsHeader zero = default; - if (!CryptoUtil.IsSameBytes(SpanHelpers.AsReadOnlyByteSpan(in header), - SpanHelpers.AsReadOnlyByteSpan(in zero), Unsafe.SizeOf())) - { - return ResultFs.InvalidNcaFsHeader.Log(); - } - } - } - - return Result.Success; - } -} - -/// -/// Handles reading information from the of a file system inside an NCA file. -/// -/// Based on nnSdk 17.5.0 (FS 17.0.0) -public class NcaFsHeaderReader17 -{ - private NcaFsHeader _header; - private int _fsIndex; - - public NcaFsHeaderReader17() - { - _fsIndex = -1; - } - - public bool IsInitialized() - { - return _fsIndex >= 0; - } - - public Result Initialize(NcaReader17 reader, int index) - { - _fsIndex = -1; - - Result res = reader.ReadHeader(out _header, index); - if (res.IsFailure()) return res.Miss(); - - Unsafe.SkipInit(out Hash hash); - IHash256GeneratorFactory generator = reader.GetHashGeneratorFactorySelector().GetFactory(HashAlgorithmType.Sha2); - generator.GenerateHash(hash.Value, SpanHelpers.AsReadOnlyByteSpan(in _header)); - - reader.GetFsHeaderHash(out Hash fsHeaderHash, index); - - if (!CryptoUtil.IsSameBytes(fsHeaderHash.Value, hash.Value, Unsafe.SizeOf())) - { - return ResultFs.NcaFsHeaderHashVerificationFailed.Log(); - } - - _fsIndex = index; - return Result.Success; - } - - public ref readonly NcaFsHeader.HashData GetHashData() - { - Assert.SdkRequires(IsInitialized()); - return ref _header.HashDataValue; - } - - public ushort GetVersion() - { - Assert.SdkRequires(IsInitialized()); - return _header.Version; - } - - public int GetFsIndex() - { - Assert.SdkRequires(IsInitialized()); - return _fsIndex; - } - - public NcaFsHeader.FsType GetFsType() - { - Assert.SdkRequires(IsInitialized()); - return _header.FsTypeValue; - } - - public NcaFsHeader.HashType GetHashType() - { - Assert.SdkRequires(IsInitialized()); - return _header.HashTypeValue; - } - - public NcaFsHeader.EncryptionType GetEncryptionType() - { - Assert.SdkRequires(IsInitialized()); - return _header.EncryptionTypeValue; - } - - public NcaFsHeader.MetaDataHashType GetPatchMetaHashType() - { - Assert.SdkRequires(IsInitialized()); - return _header.MetaDataHashTypeValue; - } - - public NcaFsHeader.MetaDataHashType GetSparseMetaHashType() - { - Assert.SdkRequires(IsInitialized()); - return _header.MetaDataHashTypeValue; - } - - public Result GetHashTargetOffset(out long outOffset) - { - Assert.SdkRequires(IsInitialized()); - - Result res = _header.GetHashTargetOffset(out outOffset); - if (res.IsFailure()) return res.Miss(); - - return Result.Success; - } - - public bool IsSkipLayerHashEncryption() - { - Assert.SdkRequires(IsInitialized()); - return _header.IsSkipLayerHashEncryption(); - } - - public ref readonly NcaPatchInfo GetPatchInfo() - { - Assert.SdkRequires(IsInitialized()); - return ref _header.PatchInfo; - } - - public NcaAesCtrUpperIv GetAesCtrUpperIv() - { - Assert.SdkRequires(IsInitialized()); - return _header.AesCtrUpperIv; - } - - public bool ExistsSparseLayer() - { - Assert.SdkRequires(IsInitialized()); - return _header.SparseInfo.Generation != 0; - } - - public ref readonly NcaSparseInfo GetSparseInfo() - { - Assert.SdkRequires(IsInitialized()); - return ref _header.SparseInfo; - } - - public bool ExistsCompressionLayer() - { - Assert.SdkRequires(IsInitialized()); - return _header.CompressionInfo.TableOffset != 0 && _header.CompressionInfo.TableSize != 0; - } - - public ref readonly NcaCompressionInfo GetCompressionInfo() - { - Assert.SdkRequires(IsInitialized()); - return ref _header.CompressionInfo; - } - - public bool ExistsPatchMetaHashLayer() - { - Assert.SdkRequires(IsInitialized()); - return _header.MetaDataHashDataInfo.Size != 0 && GetPatchInfo().HasIndirectTable(); - } - - public bool ExistsSparseMetaHashLayer() - { - Assert.SdkRequires(IsInitialized()); - return _header.MetaDataHashDataInfo.Size != 0 && ExistsSparseLayer(); - } - - public ref readonly NcaMetaDataHashDataInfo GetPatchMetaDataHashDataInfo() - { - Assert.SdkRequires(IsInitialized()); - return ref _header.MetaDataHashDataInfo; - } - - public ref readonly NcaMetaDataHashDataInfo GetSparseMetaDataHashDataInfo() - { - Assert.SdkRequires(IsInitialized()); - return ref _header.MetaDataHashDataInfo; - } - - public void GetRawData(Span outBuffer) - { - Assert.SdkRequires(IsInitialized()); - Assert.SdkRequiresLessEqual(Unsafe.SizeOf(), outBuffer.Length); - - SpanHelpers.AsReadOnlyByteSpan(in _header).CopyTo(outBuffer); - } - - public Result Verify(NcaHeader.ContentType contentType) - { - Assert.SdkRequires(IsInitialized()); - Assert.SdkRequiresWithinMinMax((int)contentType, (int)NcaHeader.ContentType.Program, (int)NcaHeader.ContentType.PublicData); - - Result res = _header.Verify(); - if (res.IsFailure()) return res.Miss(); - - const uint programSecureValue = 1; - const uint dataSecureValue = 2; - const uint htmlDocumentSecureValue = 4; - const uint legalInformationSecureValue = 5; - - // Mask out the program index part of the secure value - uint secureValue = _header.AesCtrUpperIv.SecureValue & 0xFFFFFF; - - if (GetEncryptionType() == NcaFsHeader.EncryptionType.None) - { - if (secureValue != 0) - return ResultFs.InvalidNcaFsHeader.Log(); - - return Result.Success; - } - - switch (contentType) - { - case NcaHeader.ContentType.Program: - switch (_fsIndex) - { - case 0: - if (secureValue != programSecureValue) - return ResultFs.InvalidNcaFsHeader.Log(); - break; - case 1: - if (secureValue != dataSecureValue) - return ResultFs.InvalidNcaFsHeader.Log(); - break; - default: - if (secureValue != 0) - return ResultFs.InvalidNcaFsHeader.Log(); - break; - } - - break; - case NcaHeader.ContentType.Manual: - if (secureValue != htmlDocumentSecureValue && secureValue != legalInformationSecureValue) - return ResultFs.InvalidNcaFsHeader.Log(); - break; - default: - if (secureValue != 0) - return ResultFs.InvalidNcaFsHeader.Log(); - break; - } - - return Result.Success; - } -} \ No newline at end of file diff --git a/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs b/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs index ccc6e91f..0001e223 100644 --- a/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs @@ -253,21 +253,6 @@ public class TypeLayoutTests Assert.Equal(0xC, GetOffset(in s, in s.Reserved)); } - [Fact] - public static void KeyType_Layout() - { - NcaCryptoConfiguration s = default; - - Assert.Equal(NcaCryptoConfiguration.Header1SignatureKeyGenerationMax + 1, s.Header1SignKeyModuli.Length); - Assert.Equal(NcaCryptoConfiguration.Rsa2048KeyModulusSize, s.Header1SignKeyModuli[0].Length); - Assert.Equal(NcaCryptoConfiguration.Rsa2048KeyPublicExponentSize, s.Header1SignKeyPublicExponent.Length); - Assert.Equal(NcaCryptoConfiguration.KeyAreaEncryptionKeyIndexCount, s.KeyAreaEncryptionKeySources.Length); - Assert.Equal(NcaCryptoConfiguration.Aes128KeySize, s.KeyAreaEncryptionKeySources[0].Length); - Assert.Equal(NcaCryptoConfiguration.Aes128KeySize, s.HeaderEncryptionKeySource.Length); - Assert.Equal(NcaCryptoConfiguration.HeaderEncryptionKeyCount, s.HeaderEncryptedEncryptionKeys.Length); - Assert.Equal(NcaCryptoConfiguration.Aes128KeySize, s.HeaderEncryptedEncryptionKeys[0].Length); - } - [Fact] public static void AesCtrCounterExtendedStorage_Entry_Layout() {