mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Remove the old NcaReader
This commit is contained in:
parent
e73e2861bc
commit
7f7c8b6578
10 changed files with 252 additions and 1086 deletions
|
@ -1,5 +1,7 @@
|
||||||
using LibHac.Common;
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv.FsCreator;
|
using LibHac.FsSrv.FsCreator;
|
||||||
using LibHac.FsSrv.Storage;
|
using LibHac.FsSrv.Storage;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
@ -32,14 +34,18 @@ public class DefaultFsServerObjects
|
||||||
using SharedRef<IFileSystem> sharedRootFileSystemCopy =
|
using SharedRef<IFileSystem> sharedRootFileSystemCopy =
|
||||||
SharedRef<IFileSystem>.CreateCopy(in sharedRootFileSystem);
|
SharedRef<IFileSystem>.CreateCopy(in sharedRootFileSystem);
|
||||||
|
|
||||||
|
var memoryResource = new ArrayPoolMemoryResource();
|
||||||
|
IBufferManager bufferManager = null;
|
||||||
|
IHash256GeneratorFactorySelector ncaHashGeneratorFactorySelector = null;
|
||||||
|
|
||||||
creators.RomFileSystemCreator = new RomFileSystemCreator();
|
creators.RomFileSystemCreator = new RomFileSystemCreator();
|
||||||
creators.PartitionFileSystemCreator = new PartitionFileSystemCreator();
|
creators.PartitionFileSystemCreator = new PartitionFileSystemCreator();
|
||||||
creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet);
|
creators.StorageOnNcaCreator = new StorageOnNcaCreator(memoryResource, bufferManager, InitializeNcaReader, new NcaCompressionConfiguration(), ncaHashGeneratorFactorySelector);
|
||||||
creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator();
|
creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator();
|
||||||
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
|
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
|
||||||
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, null, randomGenerator);
|
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, null, randomGenerator);
|
||||||
creators.GameCardStorageCreator = gcStorageCreator;
|
creators.GameCardStorageCreator = gcStorageCreator;
|
||||||
creators.GameCardFileSystemCreator = new GameCardFileSystemCreator(new ArrayPoolMemoryResource(), gcStorageCreator, fsServer);
|
creators.GameCardFileSystemCreator = new GameCardFileSystemCreator(memoryResource, gcStorageCreator, fsServer);
|
||||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
|
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
|
||||||
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(ref sharedRootFileSystem.Ref);
|
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(ref sharedRootFileSystem.Ref);
|
||||||
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdmmcNew, ref sharedRootFileSystemCopy.Ref);
|
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdmmcNew, ref sharedRootFileSystemCopy.Ref);
|
||||||
|
@ -55,4 +61,11 @@ public class DefaultFsServerObjects
|
||||||
StorageDeviceManagerFactory = storageDeviceManagerFactory
|
StorageDeviceManagerFactory = storageDeviceManagerFactory
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Result InitializeNcaReader(ref SharedRef<NcaReader> outReader, in SharedRef<IStorage> baseStorage,
|
||||||
|
in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector,
|
||||||
|
ContentAttributes contentAttributes)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -5,22 +5,16 @@ using LibHac.FsSystem;
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
public interface IStorageOnNcaCreator
|
public interface IStorageOnNcaCreator
|
||||||
{
|
|
||||||
Result Create(ref SharedRef<IStorage> 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<IStorage> outStorage,
|
Result Create(ref SharedRef<IStorage> outStorage,
|
||||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader,
|
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
|
||||||
ref readonly SharedRef<NcaReader17> ncaReader, int fsIndex);
|
ref readonly SharedRef<NcaReader> ncaReader, int fsIndex);
|
||||||
|
|
||||||
Result CreateWithPatch(ref SharedRef<IStorage> outStorage,
|
Result CreateWithPatch(ref SharedRef<IStorage> outStorage,
|
||||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader,
|
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
|
||||||
ref readonly SharedRef<NcaReader17> originalNcaReader, ref readonly SharedRef<NcaReader17> currentNcaReader,
|
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
|
||||||
int fsIndex);
|
int fsIndex);
|
||||||
|
|
||||||
Result CreateNcaReader(ref SharedRef<NcaReader17> outReader, ref readonly SharedRef<IStorage> baseStorage,
|
Result CreateNcaReader(ref SharedRef<NcaReader> outReader, ref readonly SharedRef<IStorage> baseStorage,
|
||||||
ContentAttributes contentAttributes);
|
ContentAttributes contentAttributes);
|
||||||
}
|
}
|
|
@ -1,77 +1,84 @@
|
||||||
using System;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Common.Keys;
|
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Tools.FsSystem;
|
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
|
||||||
using NcaFsHeader = LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader;
|
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the partitions in NCAs as <see cref="IStorage"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
||||||
public class StorageOnNcaCreator : IStorageOnNcaCreator
|
public class StorageOnNcaCreator : IStorageOnNcaCreator
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedMember.Local
|
private MemoryResource _memoryResource;
|
||||||
private bool IsEnabledProgramVerification { get; set; }
|
private NcaCompressionConfiguration _compressionConfig;
|
||||||
private KeySet KeySet { get; }
|
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<IStorage> outStorage,
|
||||||
public Result Create(ref SharedRef<IStorage> outStorage, out NcaFsHeader fsHeader, Nca nca,
|
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
|
||||||
int fsIndex, bool isCodeFs)
|
ref readonly SharedRef<NcaReader> 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<IStorage>();
|
||||||
|
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||||
|
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
|
||||||
|
ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex));
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
if (isCodeFs)
|
using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
|
||||||
{
|
|
||||||
using var codeFs = new PartitionFileSystem();
|
|
||||||
res = codeFs.Initialize(storageTemp);
|
|
||||||
if (res.IsFailure()) return res.Miss();
|
|
||||||
|
|
||||||
res = VerifyAcidSignature(codeFs, nca);
|
outStorage.SetByMove(ref resultConvertStorage.Ref);
|
||||||
if (res.IsFailure()) return res.Miss();
|
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
|
||||||
}
|
|
||||||
|
|
||||||
outStorage.Reset(storageTemp);
|
|
||||||
fsHeader = nca.GetFsHeader(fsIndex);
|
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateWithPatch(ref SharedRef<IStorage> outStorage, out NcaFsHeader fsHeader,
|
public Result CreateWithPatch(ref SharedRef<IStorage> outStorage,
|
||||||
Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs)
|
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
|
||||||
|
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
|
||||||
|
int fsIndex)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var ncaFsDriver = new NcaFileSystemDriver(in originalNcaReader, in currentNcaReader, _memoryResource,
|
||||||
}
|
_bufferManager, _hashGeneratorFactorySelector);
|
||||||
|
|
||||||
|
using var storage = new SharedRef<IStorage>();
|
||||||
|
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||||
|
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<RomResultConvertStorage>(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;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result VerifyAcidSignature(IFileSystem codeFileSystem, Nca nca)
|
public Result CreateNcaReader(ref SharedRef<NcaReader> outReader, ref readonly SharedRef<IStorage> baseStorage,
|
||||||
|
ContentAttributes contentAttributes)
|
||||||
{
|
{
|
||||||
// todo
|
using var ncaReader = new SharedRef<NcaReader>();
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result OpenStorage(out IStorage storage, Nca nca, int fsIndex)
|
Result res = RomResultConverter.ConvertRomResult(_ncaReaderInitializer(ref ncaReader.Ref, in baseStorage,
|
||||||
{
|
in _compressionConfig, _hashGeneratorFactorySelector, contentAttributes));
|
||||||
UnsafeHelpers.SkipParamInit(out storage);
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
if (!nca.SectionExists(fsIndex))
|
outReader.SetByMove(ref ncaReader.Ref);
|
||||||
return ResultFs.PartitionNotFound.Log();
|
|
||||||
|
|
||||||
storage = nca.OpenStorage(fsIndex, IntegrityCheckLevel.ErrorOnInvalid);
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Fs;
|
|
||||||
using LibHac.FsSystem;
|
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the partitions in NCAs as <see cref="IStorage"/>s.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
|
||||||
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<IStorage> outStorage,
|
|
||||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader,
|
|
||||||
ref readonly SharedRef<NcaReader17> ncaReader, int fsIndex)
|
|
||||||
{
|
|
||||||
var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector);
|
|
||||||
|
|
||||||
using var storage = new SharedRef<IStorage>();
|
|
||||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
|
||||||
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<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
|
|
||||||
|
|
||||||
outStorage.SetByMove(ref resultConvertStorage.Ref);
|
|
||||||
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CreateWithPatch(ref SharedRef<IStorage> outStorage,
|
|
||||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader,
|
|
||||||
ref readonly SharedRef<NcaReader17> originalNcaReader, ref readonly SharedRef<NcaReader17> currentNcaReader,
|
|
||||||
int fsIndex)
|
|
||||||
{
|
|
||||||
var ncaFsDriver = new NcaFileSystemDriver(in originalNcaReader, in currentNcaReader, _memoryResource,
|
|
||||||
_bufferManager, _hashGeneratorFactorySelector);
|
|
||||||
|
|
||||||
using var storage = new SharedRef<IStorage>();
|
|
||||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
|
||||||
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<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
|
|
||||||
|
|
||||||
outStorage.SetByMove(ref resultConvertStorage.Ref);
|
|
||||||
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CreateNcaReader(ref SharedRef<NcaReader17> outReader, ref readonly SharedRef<IStorage> baseStorage,
|
|
||||||
ContentAttributes contentAttributes)
|
|
||||||
{
|
|
||||||
using var ncaReader = new SharedRef<NcaReader17>();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,8 +13,6 @@ using LibHac.Spl;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
using static LibHac.Fs.Impl.CommonMountNames;
|
using static LibHac.Fs.Impl.CommonMountNames;
|
||||||
using NcaFsHeader = LibHac.Tools.FsSystem.NcaUtils.NcaFsHeader;
|
|
||||||
using NcaHeader = LibHac.FsSystem.NcaHeader;
|
|
||||||
using RightsId = LibHac.Fs.RightsId;
|
using RightsId = LibHac.Fs.RightsId;
|
||||||
using Utility = LibHac.FsSystem.Utility;
|
using Utility = LibHac.FsSystem.Utility;
|
||||||
|
|
||||||
|
@ -272,9 +270,10 @@ public class NcaFileSystemServiceImpl
|
||||||
{
|
{
|
||||||
using SharedRef<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.CreateMove(ref subDirFs.Ref);
|
using SharedRef<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.CreateMove(ref subDirFs.Ref);
|
||||||
res = _config.EncryptedFsCreator.Create(ref subDirFs.Ref, ref tempFileSystem.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();
|
if (res.IsFailure()) return res.Miss();
|
||||||
}
|
}
|
||||||
|
|
||||||
outFileSystem.SetByMove(ref subDirFs.Ref);
|
outFileSystem.SetByMove(ref subDirFs.Ref);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -355,7 +354,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, ContentStorageSystemMountName,
|
else if (StringUtils.Compare(path, ContentStorageSystemMountName,
|
||||||
ContentStorageSystemMountName.Length) == 0)
|
ContentStorageSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(ContentStorageSystemMountName.Length);
|
path = path.Slice(ContentStorageSystemMountName.Length);
|
||||||
|
|
||||||
|
@ -366,7 +365,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, ContentStorageUserMountName,
|
else if (StringUtils.Compare(path, ContentStorageUserMountName,
|
||||||
ContentStorageUserMountName.Length) == 0)
|
ContentStorageUserMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(ContentStorageUserMountName.Length);
|
path = path.Slice(ContentStorageUserMountName.Length);
|
||||||
|
|
||||||
|
@ -377,7 +376,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, ContentStorageSdCardMountName,
|
else if (StringUtils.Compare(path, ContentStorageSdCardMountName,
|
||||||
ContentStorageSdCardMountName.Length) == 0)
|
ContentStorageSdCardMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(ContentStorageSdCardMountName.Length);
|
path = path.Slice(ContentStorageSdCardMountName.Length);
|
||||||
|
|
||||||
|
@ -388,7 +387,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, BisCalibrationFilePartitionMountName,
|
else if (StringUtils.Compare(path, BisCalibrationFilePartitionMountName,
|
||||||
BisCalibrationFilePartitionMountName.Length) == 0)
|
BisCalibrationFilePartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(BisCalibrationFilePartitionMountName.Length);
|
path = path.Slice(BisCalibrationFilePartitionMountName.Length);
|
||||||
|
|
||||||
|
@ -397,7 +396,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, BisSafeModePartitionMountName,
|
else if (StringUtils.Compare(path, BisSafeModePartitionMountName,
|
||||||
BisSafeModePartitionMountName.Length) == 0)
|
BisSafeModePartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(BisSafeModePartitionMountName.Length);
|
path = path.Slice(BisSafeModePartitionMountName.Length);
|
||||||
|
|
||||||
|
@ -406,7 +405,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, BisUserPartitionMountName,
|
else if (StringUtils.Compare(path, BisUserPartitionMountName,
|
||||||
BisUserPartitionMountName.Length) == 0)
|
BisUserPartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(BisUserPartitionMountName.Length);
|
path = path.Slice(BisUserPartitionMountName.Length);
|
||||||
|
|
||||||
|
@ -415,7 +414,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, BisSystemPartitionMountName,
|
else if (StringUtils.Compare(path, BisSystemPartitionMountName,
|
||||||
BisSystemPartitionMountName.Length) == 0)
|
BisSystemPartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(BisSystemPartitionMountName.Length);
|
path = path.Slice(BisSystemPartitionMountName.Length);
|
||||||
|
|
||||||
|
@ -424,7 +423,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, SdCardFileSystemMountName,
|
else if (StringUtils.Compare(path, SdCardFileSystemMountName,
|
||||||
SdCardFileSystemMountName.Length) == 0)
|
SdCardFileSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(SdCardFileSystemMountName.Length);
|
path = path.Slice(SdCardFileSystemMountName.Length);
|
||||||
|
|
||||||
|
@ -433,7 +432,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, HostRootFileSystemMountName,
|
else if (StringUtils.Compare(path, HostRootFileSystemMountName,
|
||||||
HostRootFileSystemMountName.Length) == 0)
|
HostRootFileSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(HostRootFileSystemMountName.Length);
|
path = path.Slice(HostRootFileSystemMountName.Length);
|
||||||
|
|
||||||
|
@ -449,7 +448,7 @@ public class NcaFileSystemServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, RegisteredUpdatePartitionMountName,
|
else if (StringUtils.Compare(path, RegisteredUpdatePartitionMountName,
|
||||||
RegisteredUpdatePartitionMountName.Length) == 0)
|
RegisteredUpdatePartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(RegisteredUpdatePartitionMountName.Length);
|
path = path.Slice(RegisteredUpdatePartitionMountName.Length);
|
||||||
|
|
||||||
|
@ -599,34 +598,7 @@ public class NcaFileSystemServiceImpl
|
||||||
|
|
||||||
private Result ParseNca(ref U8Span path, out Nca nca, ref SharedRef<IFileSystem> baseFileSystem, ulong ncaId)
|
private Result ParseNca(ref U8Span path, out Nca nca, ref SharedRef<IFileSystem> baseFileSystem, ulong ncaId)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out nca);
|
throw new NotImplementedException();
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result ParseContentTypeForDirectory(ref SharedRef<IFileSystem> outFileSystem,
|
private Result ParseContentTypeForDirectory(ref SharedRef<IFileSystem> outFileSystem,
|
||||||
|
@ -696,63 +668,7 @@ public class NcaFileSystemServiceImpl
|
||||||
private Result OpenStorageByContentType(ref SharedRef<IStorage> outNcaStorage, Nca nca,
|
private Result OpenStorageByContentType(ref SharedRef<IStorage> outNcaStorage, Nca nca,
|
||||||
out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate)
|
out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out fsType);
|
throw new NotImplementedException();
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||||
|
|
|
@ -9,6 +9,8 @@ using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
namespace LibHac.FsSystem;
|
||||||
|
|
||||||
|
public delegate Result CryptAesXtsFunction(Span<byte> dest, ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> source);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads and writes to an <see cref="IStorage"/> that's encrypted with AES-XTS-128.
|
/// Reads and writes to an <see cref="IStorage"/> that's encrypted with AES-XTS-128.
|
||||||
/// All encryption or decryption will be done externally via a provided function.
|
/// All encryption or decryption will be done externally via a provided function.
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
// ReSharper disable UnusedMember.Local
|
using System;
|
||||||
using System;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Common.FixedArrays;
|
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv;
|
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
namespace LibHac.FsSystem;
|
||||||
|
|
||||||
|
@ -30,20 +27,6 @@ public struct NcaCryptoConfiguration
|
||||||
|
|
||||||
public const int KeyGenerationMax = 32;
|
public const int KeyGenerationMax = 32;
|
||||||
public const int KeyAreaEncryptionKeyCount = KeyAreaEncryptionKeyIndexCount * KeyGenerationMax;
|
public const int KeyAreaEncryptionKeyCount = KeyAreaEncryptionKeyIndexCount * KeyGenerationMax;
|
||||||
|
|
||||||
public Array2<Array256<byte>> Header1SignKeyModuli;
|
|
||||||
public Array3<byte> Header1SignKeyPublicExponent;
|
|
||||||
public Array3<Array16<byte>> KeyAreaEncryptionKeySources;
|
|
||||||
public Array16<byte> HeaderEncryptionKeySource;
|
|
||||||
public Array2<Array16<byte>> 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
|
public struct NcaCompressionConfiguration
|
||||||
|
@ -88,12 +71,12 @@ public enum KeyType
|
||||||
|
|
||||||
file static class Anonymous
|
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);
|
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);
|
return (long)reader.GetFsEndOffset(index);
|
||||||
}
|
}
|
||||||
|
@ -102,12 +85,12 @@ file static class Anonymous
|
||||||
file class SharedNcaBodyStorage : IStorage
|
file class SharedNcaBodyStorage : IStorage
|
||||||
{
|
{
|
||||||
private SharedRef<IStorage> _storage;
|
private SharedRef<IStorage> _storage;
|
||||||
private SharedRef<NcaReader17> _ncaReader;
|
private SharedRef<NcaReader> _ncaReader;
|
||||||
|
|
||||||
public SharedNcaBodyStorage(in SharedRef<IStorage> baseStorage, in SharedRef<NcaReader17> ncaReader)
|
public SharedNcaBodyStorage(in SharedRef<IStorage> baseStorage, in SharedRef<NcaReader> ncaReader)
|
||||||
{
|
{
|
||||||
_storage = SharedRef<IStorage>.CreateCopy(in baseStorage);
|
_storage = SharedRef<IStorage>.CreateCopy(in baseStorage);
|
||||||
_ncaReader = SharedRef<NcaReader17>.CreateCopy(in ncaReader);
|
_ncaReader = SharedRef<NcaReader>.CreateCopy(in ncaReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
|
@ -202,14 +185,14 @@ public class NcaFileSystemDriver : IDisposable
|
||||||
None = 1
|
None = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public NcaFileSystemDriver(ref readonly SharedRef<NcaReader17> ncaReader, MemoryResource allocator,
|
public NcaFileSystemDriver(ref readonly SharedRef<NcaReader> ncaReader, MemoryResource allocator,
|
||||||
IBufferManager bufferManager, IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
IBufferManager bufferManager, IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NcaFileSystemDriver(ref readonly SharedRef<NcaReader17> originalNcaReader,
|
public NcaFileSystemDriver(ref readonly SharedRef<NcaReader> originalNcaReader,
|
||||||
ref readonly SharedRef<NcaReader17> currentNcaReader, MemoryResource allocator, IBufferManager bufferManager,
|
ref readonly SharedRef<NcaReader> currentNcaReader, MemoryResource allocator, IBufferManager bufferManager,
|
||||||
IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
@ -221,20 +204,20 @@ public class NcaFileSystemDriver : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenStorage(ref SharedRef<IStorage> outStorage,
|
public Result OpenStorage(ref SharedRef<IStorage> outStorage,
|
||||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader17 outHeaderReader,
|
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
|
||||||
int fsIndex)
|
int fsIndex)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result OpenStorageImpl(ref SharedRef<IStorage> outStorage, out NcaFsHeaderReader17 outHeaderReader,
|
private Result OpenStorageImpl(ref SharedRef<IStorage> outStorage, out NcaFsHeaderReader outHeaderReader,
|
||||||
int fsIndex, ref StorageContext storageContext)
|
int fsIndex, ref StorageContext storageContext)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result OpenIndirectableStorageAsOriginal(ref SharedRef<IStorage> outStorage,
|
private Result OpenIndirectableStorageAsOriginal(ref SharedRef<IStorage> outStorage,
|
||||||
in NcaFsHeaderReader17 headerReader, ref StorageContext storageContext)
|
in NcaFsHeaderReader headerReader, ref StorageContext storageContext)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -375,7 +358,7 @@ public class NcaFileSystemDriver : IDisposable
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result CreateRegionSwitchStorage(ref SharedRef<IStorage> outStorage, in NcaFsHeaderReader17 headerReader,
|
private Result CreateRegionSwitchStorage(ref SharedRef<IStorage> outStorage, in NcaFsHeaderReader headerReader,
|
||||||
ref readonly SharedRef<IStorage> insideRegionStorage, ref readonly SharedRef<IStorage> outsideRegionStorage)
|
ref readonly SharedRef<IStorage> insideRegionStorage, ref readonly SharedRef<IStorage> outsideRegionStorage)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
|
@ -1,201 +1,53 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Common.FixedArrays;
|
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.Spl;
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
namespace LibHac.FsSystem;
|
||||||
|
|
||||||
public delegate void GenerateKeyFunction(Span<byte> destKey, ReadOnlySpan<byte> sourceKey, int keyType);
|
public delegate Result NcaReaderInitializer(ref SharedRef<NcaReader> outReader, in SharedRef<IStorage> baseStorage,
|
||||||
public delegate Result DecryptAesCtrFunction(Span<byte> dest, int keyIndex, int keyGeneration, ReadOnlySpan<byte> encryptedKey, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> source);
|
in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector,
|
||||||
public delegate Result CryptAesXtsFunction(Span<byte> dest, ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> source);
|
ContentAttributes contentAttributes);
|
||||||
public delegate bool VerifySign1Function(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> data, bool isProd, byte generation);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles reading information from an NCA file's header.
|
/// Handles reading information from an NCA's header.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
|
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
||||||
public class NcaReader : IDisposable
|
public class NcaReader : IDisposable
|
||||||
{
|
{
|
||||||
private const uint SdkAddonVersionMin = 0xB0000;
|
private RuntimeNcaHeader _header;
|
||||||
|
|
||||||
private NcaHeader _header;
|
|
||||||
private Array5<Array16<byte>> _decryptionKeys;
|
|
||||||
private SharedRef<IStorage> _bodyStorage;
|
private SharedRef<IStorage> _bodyStorage;
|
||||||
private UniqueRef<IStorage> _headerStorage;
|
private SharedRef<IStorage> _headerStorage;
|
||||||
private Array16<byte> _externalDataDecryptionKey;
|
private SharedRef<IAesCtrDecryptor> _aesCtrDecryptor;
|
||||||
private DecryptAesCtrFunction _decryptAesCtr;
|
|
||||||
private DecryptAesCtrFunction _decryptAesCtrForExternalKey;
|
|
||||||
private bool _isSoftwareAesPrioritized;
|
|
||||||
private bool _isAvailableSwKey;
|
|
||||||
private NcaHeader.EncryptionType _headerEncryptionType;
|
|
||||||
private GetDecompressorFunction _getDecompressorFunc;
|
private GetDecompressorFunction _getDecompressorFunc;
|
||||||
private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector;
|
private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector;
|
||||||
|
|
||||||
public void Dispose()
|
public NcaReader(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef<IStorage> notVerifiedHeaderStorage,
|
||||||
{
|
ref readonly SharedRef<IStorage> bodyStorage, ref readonly SharedRef<IAesCtrDecryptor> aesCtrDecryptor,
|
||||||
_headerStorage.Destroy();
|
|
||||||
_bodyStorage.Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Initialize(ref SharedRef<IStorage> baseStorage, in NcaCryptoConfiguration cryptoConfig,
|
|
||||||
in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(in baseStorage);
|
Assert.SdkRequiresNotNull(in notVerifiedHeaderStorage);
|
||||||
|
Assert.SdkRequiresNotNull(in bodyStorage);
|
||||||
Assert.SdkRequiresNotNull(hashGeneratorFactorySelector);
|
Assert.SdkRequiresNotNull(hashGeneratorFactorySelector);
|
||||||
Assert.SdkRequiresNull(in _bodyStorage);
|
|
||||||
|
|
||||||
if (cryptoConfig.VerifySign1 is null)
|
_header = runtimeNcaHeader;
|
||||||
return ResultFs.InvalidArgument.Log();
|
|
||||||
|
|
||||||
using var headerStorage = new UniqueRef<IStorage>();
|
_headerStorage = SharedRef<IStorage>.CreateCopy(in notVerifiedHeaderStorage);
|
||||||
|
_bodyStorage = SharedRef<IStorage>.CreateCopy(in bodyStorage);
|
||||||
|
_aesCtrDecryptor = SharedRef<IAesCtrDecryptor>.CreateCopy(in aesCtrDecryptor);
|
||||||
|
|
||||||
if (cryptoConfig.IsAvailableSwKey)
|
|
||||||
{
|
|
||||||
if (cryptoConfig.GenerateKey is null)
|
|
||||||
return ResultFs.InvalidArgument.Log();
|
|
||||||
|
|
||||||
ReadOnlySpan<int> headerKeyTypes = stackalloc int[NcaCryptoConfiguration.HeaderEncryptionKeyCount]
|
|
||||||
{ (int)KeyType.NcaHeaderKey1, (int)KeyType.NcaHeaderKey2 };
|
|
||||||
|
|
||||||
// Generate the keys for decrypting the NCA header.
|
|
||||||
Unsafe.SkipInit(out Array2<Array16<byte>> 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<byte> 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<byte> headerIv = default;
|
|
||||||
headerStorage.Reset(new AesXtsStorageExternal(baseStorage.Get, ReadOnlySpan<byte>.Empty,
|
|
||||||
ReadOnlySpan<byte>.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<byte> signature = _header.Signature1;
|
|
||||||
ReadOnlySpan<byte> 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<byte> 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<byte> 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<byte> 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;
|
_getDecompressorFunc = compressionConfig.GetDecompressorFunc;
|
||||||
|
_hashGeneratorFactorySelector = hashGeneratorFactorySelector;
|
||||||
|
}
|
||||||
|
|
||||||
_bodyStorage.SetByMove(ref baseStorage);
|
public void Dispose()
|
||||||
_headerStorage.Set(ref headerStorage.Ref);
|
{
|
||||||
|
_bodyStorage.Destroy();
|
||||||
return Result.Success;
|
_headerStorage.Destroy();
|
||||||
|
_aesCtrDecryptor.Destroy();
|
||||||
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 Result ReadHeader(out NcaFsHeader outHeader, int index)
|
public Result ReadHeader(out NcaFsHeader outHeader, int index)
|
||||||
|
@ -204,29 +56,23 @@ public class NcaReader : IDisposable
|
||||||
|
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
||||||
|
|
||||||
long offset = Unsafe.SizeOf<NcaHeader>() + Unsafe.SizeOf<NcaFsHeader>() * (long)index;
|
long offset = _header.FsHeadersOffset + Unsafe.SizeOf<NcaFsHeader>() * (long)index;
|
||||||
return _headerStorage.Get.Read(offset, SpanHelpers.AsByteSpan(ref outHeader));
|
return _headerStorage.Get.Read(offset, SpanHelpers.AsByteSpan(ref outHeader)).Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetHeaderSign2(Span<byte> outBuffer)
|
public Result GetHeaderSign2(Span<byte> 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<byte> outBuffer)
|
public void GetHeaderSign2TargetHash(Span<byte> outBuffer)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_hashGeneratorFactorySelector);
|
Assert.SdkRequiresEqual(outBuffer.Length, Unsafe.SizeOf<Hash>());
|
||||||
Assert.SdkRequiresEqual(IHash256Generator.HashSize, outBuffer.Length);
|
|
||||||
|
|
||||||
int signTargetOffset = NcaHeader.HeaderSignSize * NcaHeader.HeaderSignCount;
|
_header.Header2SignInfo.Hash.Value[..].CopyTo(outBuffer);
|
||||||
int signTargetSize = NcaHeader.Size - signTargetOffset;
|
|
||||||
ReadOnlySpan<byte> signTarget =
|
|
||||||
SpanHelpers.AsReadOnlyByteSpan(in _header).Slice(signTargetOffset, signTargetSize);
|
|
||||||
|
|
||||||
IHash256GeneratorFactory factory = _hashGeneratorFactorySelector.GetFactory(HashAlgorithmType.Sha2);
|
|
||||||
factory.GenerateHash(outBuffer, signTarget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedRef<IStorage> GetSharedBodyStorage()
|
public SharedRef<IStorage> GetSharedBodyStorage()
|
||||||
|
@ -236,60 +82,26 @@ public class NcaReader : IDisposable
|
||||||
return SharedRef<IStorage>.CreateCopy(in _bodyStorage);
|
return SharedRef<IStorage>.CreateCopy(in _bodyStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetSignature()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
return _header.Magic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NcaHeader.DistributionType GetDistributionType()
|
public NcaHeader.DistributionType GetDistributionType()
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
return _header.DistributionType;
|
||||||
return _header.DistributionTypeValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NcaHeader.ContentType GetContentType()
|
public NcaHeader.ContentType GetContentType()
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
return _header.ContentType;
|
||||||
return _header.ContentTypeValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte GetKeyGeneration()
|
public byte GetKeyGeneration()
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
return _header.KeyGeneration;
|
||||||
return _header.GetProperKeyGeneration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte GetKeyIndex()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
return _header.KeyAreaEncryptionKeyIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong GetContentSize()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
return _header.ContentSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetProgramId()
|
public ulong GetProgramId()
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
return _header.ProgramId;
|
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<byte> outBuffer)
|
public void GetRightsId(Span<byte> outBuffer)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresGreaterEqual(outBuffer.Length, NcaHeader.RightsIdSize);
|
Assert.SdkRequiresGreaterEqual(outBuffer.Length, NcaHeader.RightsIdSize);
|
||||||
|
@ -300,57 +112,30 @@ public class NcaReader : IDisposable
|
||||||
public bool HasFsInfo(int index)
|
public bool HasFsInfo(int index)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
||||||
|
|
||||||
return _header.FsInfos[index].StartSector != 0 || _header.FsInfos[index].EndSector != 0;
|
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)
|
public void GetFsHeaderHash(out Hash outHash, int index)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
||||||
|
outHash = _header.FsInfos[index].Hash;
|
||||||
outHash = _header.FsHeaderHashes[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetFsInfo(out NcaHeader.FsInfo outFsInfo, int index)
|
public void GetFsInfo(out NcaHeader.FsInfo outFsInfo, int index)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
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)
|
public ulong GetFsOffset(int index)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
||||||
|
|
||||||
return NcaHeader.SectorToByte(_header.FsInfos[index].StartSector);
|
return NcaHeader.SectorToByte(_header.FsInfos[index].StartSector);
|
||||||
|
@ -358,7 +143,6 @@ public class NcaReader : IDisposable
|
||||||
|
|
||||||
public ulong GetFsEndOffset(int index)
|
public ulong GetFsEndOffset(int index)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
||||||
|
|
||||||
return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector);
|
return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector);
|
||||||
|
@ -366,102 +150,35 @@ public class NcaReader : IDisposable
|
||||||
|
|
||||||
public ulong GetFsSize(int index)
|
public ulong GetFsSize(int index)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
Assert.SdkRequiresInRange(index, 0, NcaHeader.FsCountMax);
|
||||||
|
|
||||||
return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector - _header.FsInfos[index].StartSector);
|
return NcaHeader.SectorToByte(_header.FsInfos[index].EndSector - _header.FsInfos[index].StartSector);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetEncryptedKey(Span<byte> outBuffer)
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresGreaterEqual(outBuffer.Length, NcaHeader.EncryptedKeyAreaSize);
|
|
||||||
|
|
||||||
_header.EncryptedKeys[..].CopyTo(outBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetDecryptionKey(int index)
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
Assert.SdkRequiresInRange(index, 0, (int)NcaHeader.DecryptionKey.Count);
|
|
||||||
|
|
||||||
return _decryptionKeys[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasValidInternalKey()
|
|
||||||
{
|
|
||||||
Array16<byte> 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<byte> zeroKey = default;
|
|
||||||
return !CryptoUtil.IsSameBytes(zeroKey, GetDecryptionKey((int)NcaHeader.DecryptionKey.AesCtrHw),
|
|
||||||
zeroKey.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSwAesPrioritized()
|
|
||||||
{
|
|
||||||
return _isSoftwareAesPrioritized;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PrioritizeSwAes()
|
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<byte> key)
|
public RuntimeNcaHeader GetHeader()
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresEqual(_externalDataDecryptionKey[..].Length, key.Length);
|
return _header;
|
||||||
|
|
||||||
key.CopyTo(_externalDataDecryptionKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetExternalDecryptionKey()
|
public SharedRef<IAesCtrDecryptor> GetDecryptor()
|
||||||
{
|
{
|
||||||
return _externalDataDecryptionKey;
|
return SharedRef<IAesCtrDecryptor>.CreateCopy(in _aesCtrDecryptor);
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasExternalDecryptionKey()
|
|
||||||
{
|
|
||||||
Array16<byte> zeroKey = default;
|
|
||||||
return !CryptoUtil.IsSameBytes(zeroKey, GetExternalDecryptionKey(), zeroKey.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetRawData(Span<byte> outBuffer)
|
|
||||||
{
|
|
||||||
Assert.SdkRequires(_bodyStorage.HasValue);
|
|
||||||
Assert.SdkRequiresLessEqual(Unsafe.SizeOf<NcaHeader>(), outBuffer.Length);
|
|
||||||
|
|
||||||
SpanHelpers.AsReadOnlyByteSpan(in _header).CopyTo(outBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DecryptAesCtrFunction GetExternalDecryptAesCtrFunction()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_decryptAesCtr);
|
|
||||||
return _decryptAesCtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DecryptAesCtrFunction GetExternalDecryptAesCtrFunctionForExternalKey()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_decryptAesCtrForExternalKey);
|
|
||||||
return _decryptAesCtrForExternalKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetDecompressorFunction GetDecompressor()
|
public GetDecompressorFunction GetDecompressor()
|
||||||
|
@ -475,12 +192,44 @@ public class NcaReader : IDisposable
|
||||||
Assert.SdkRequiresNotNull(_hashGeneratorFactorySelector);
|
Assert.SdkRequiresNotNull(_hashGeneratorFactorySelector);
|
||||||
return _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<NcaFsHeader>()))
|
||||||
|
{
|
||||||
|
return ResultFs.InvalidNcaFsHeader.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles reading information from the <see cref="NcaFsHeader"/> of a file system inside an NCA file.
|
/// Handles reading information from the <see cref="NcaFsHeader"/> of a file system inside an NCA file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
|
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
||||||
public class NcaFsHeaderReader
|
public class NcaFsHeaderReader
|
||||||
{
|
{
|
||||||
private NcaFsHeader _header;
|
private NcaFsHeader _header;
|
||||||
|
@ -507,7 +256,9 @@ public class NcaFsHeaderReader
|
||||||
IHash256GeneratorFactory generator = reader.GetHashGeneratorFactorySelector().GetFactory(HashAlgorithmType.Sha2);
|
IHash256GeneratorFactory generator = reader.GetHashGeneratorFactorySelector().GetFactory(HashAlgorithmType.Sha2);
|
||||||
generator.GenerateHash(hash.Value, SpanHelpers.AsReadOnlyByteSpan(in _header));
|
generator.GenerateHash(hash.Value, SpanHelpers.AsReadOnlyByteSpan(in _header));
|
||||||
|
|
||||||
if (!CryptoUtil.IsSameBytes(reader.GetFsHeaderHash(index).Value, hash.Value, Unsafe.SizeOf<Hash>()))
|
reader.GetFsHeaderHash(out Hash fsHeaderHash, index);
|
||||||
|
|
||||||
|
if (!CryptoUtil.IsSameBytes(fsHeaderHash.Value, hash.Value, Unsafe.SizeOf<Hash>()))
|
||||||
{
|
{
|
||||||
return ResultFs.NcaFsHeaderHashVerificationFailed.Log();
|
return ResultFs.NcaFsHeaderHashVerificationFailed.Log();
|
||||||
}
|
}
|
||||||
|
@ -647,4 +398,61 @@ public class NcaFsHeaderReader
|
||||||
|
|
||||||
SpanHelpers.AsReadOnlyByteSpan(in _header).CopyTo(outBuffer);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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<NcaReader17> outReader, in SharedRef<IStorage> baseStorage,
|
|
||||||
in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector,
|
|
||||||
ContentAttributes contentAttributes);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles reading information from an NCA's header.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
|
||||||
public class NcaReader17 : IDisposable
|
|
||||||
{
|
|
||||||
private RuntimeNcaHeader _header;
|
|
||||||
private SharedRef<IStorage> _bodyStorage;
|
|
||||||
private SharedRef<IStorage> _headerStorage;
|
|
||||||
private SharedRef<IAesCtrDecryptor> _aesCtrDecryptor;
|
|
||||||
private GetDecompressorFunction _getDecompressorFunc;
|
|
||||||
private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector;
|
|
||||||
|
|
||||||
public NcaReader17(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef<IStorage> notVerifiedHeaderStorage,
|
|
||||||
ref readonly SharedRef<IStorage> bodyStorage, ref readonly SharedRef<IAesCtrDecryptor> aesCtrDecryptor,
|
|
||||||
in NcaCompressionConfiguration compressionConfig, IHash256GeneratorFactorySelector hashGeneratorFactorySelector)
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(in notVerifiedHeaderStorage);
|
|
||||||
Assert.SdkRequiresNotNull(in bodyStorage);
|
|
||||||
Assert.SdkRequiresNotNull(hashGeneratorFactorySelector);
|
|
||||||
|
|
||||||
_header = runtimeNcaHeader;
|
|
||||||
|
|
||||||
_headerStorage = SharedRef<IStorage>.CreateCopy(in notVerifiedHeaderStorage);
|
|
||||||
_bodyStorage = SharedRef<IStorage>.CreateCopy(in bodyStorage);
|
|
||||||
_aesCtrDecryptor = SharedRef<IAesCtrDecryptor>.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<NcaFsHeader>() * (long)index;
|
|
||||||
return _headerStorage.Get.Read(offset, SpanHelpers.AsByteSpan(ref outHeader)).Ret();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetHeaderSign2(Span<byte> 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<byte> outBuffer)
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresEqual(outBuffer.Length, Unsafe.SizeOf<Hash>());
|
|
||||||
|
|
||||||
_header.Header2SignInfo.Hash.Value[..].CopyTo(outBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SharedRef<IStorage> GetSharedBodyStorage()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresNotNull(_bodyStorage);
|
|
||||||
|
|
||||||
return SharedRef<IStorage>.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<byte> 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<IAesCtrDecryptor> GetDecryptor()
|
|
||||||
{
|
|
||||||
return SharedRef<IAesCtrDecryptor>.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<NcaFsHeader>()))
|
|
||||||
{
|
|
||||||
return ResultFs.InvalidNcaFsHeader.Log();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles reading information from the <see cref="NcaFsHeader"/> of a file system inside an NCA file.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
|
||||||
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<Hash>()))
|
|
||||||
{
|
|
||||||
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<byte> outBuffer)
|
|
||||||
{
|
|
||||||
Assert.SdkRequires(IsInitialized());
|
|
||||||
Assert.SdkRequiresLessEqual(Unsafe.SizeOf<NcaFsHeader>(), 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -253,21 +253,6 @@ public class TypeLayoutTests
|
||||||
Assert.Equal(0xC, GetOffset(in s, in s.Reserved));
|
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]
|
[Fact]
|
||||||
public static void AesCtrCounterExtendedStorage_Entry_Layout()
|
public static void AesCtrCounterExtendedStorage_Entry_Layout()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue