public IStorage OpenStorageWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorageWithPatch(patchNca, index); NcaFsHeader header = patchNca.GetFsHeader(index); return(CreateVerificationStorage(integrityCheckLevel, header, rawStorage)); }
public IFileSystem OpenFileSystem(int index, IntegrityCheckLevel integrityCheckLevel) { IStorage storage = OpenStorage(index, integrityCheckLevel); NcaFsHeader header = Header.GetFsHeader(index); return(OpenFileSystem(storage, header)); }
private IStorage OpenAesCtrExStorage(IStorage baseStorage, int index) { NcaFsHeader fsHeader = Header.GetFsHeader(index); NcaFsPatchInfo info = fsHeader.GetPatchInfo(); long sectionOffset = Header.GetSectionStartOffset(index); long sectionSize = Header.GetSectionSize(index); long bktrOffset = info.RelocationTreeOffset; long bktrSize = sectionSize - bktrOffset; long dataSize = info.RelocationTreeOffset; byte[] key = GetContentKey(NcaKeyType.AesCtr); byte[] counter = Aes128CtrStorage.CreateCounter(fsHeader.Counter, bktrOffset + sectionOffset); byte[] counterEx = Aes128CtrStorage.CreateCounter(fsHeader.Counter, sectionOffset); IStorage bucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true); IStorage encryptionBucketTreeData = bucketTreeData.Slice(info.EncryptionTreeOffset - bktrOffset); IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), encryptionBucketTreeData, key, counterEx, true); decStorage = new CachedStorage(decStorage, 0x4000, 4, true); return(new ConcatenationStorage(new[] { decStorage, bucketTreeData }, true)); }
public IFileSystem OpenFileSystemWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage storage = OpenStorageWithPatch(patchNca, index, integrityCheckLevel); NcaFsHeader header = patchNca.Header.GetFsHeader(index); return(OpenFileSystem(storage, header)); }
public static Validity VerifySection(this Nca nca, Nca patchNca, int index, IProgressReport logger = null, bool quiet = false) { NcaFsHeader sect = nca.Header.GetFsHeader(index); NcaHashType hashType = sect.HashType; if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) { return(Validity.Unchecked); } var stream = nca.OpenStorageWithPatch(patchNca, index, IntegrityCheckLevel.IgnoreOnInvalid) as HierarchicalIntegrityVerificationStorage; if (stream == null) { return(Validity.Unchecked); } if (!quiet) { logger?.LogMessage($"Verifying section {index}..."); } Validity validity = stream.Validate(true, logger); return(validity); }
// ReSharper restore UnusedParameter.Local private IStorage OpenAesCtrStorage(IStorage baseStorage, int index) { NcaFsHeader fsHeader = Header.GetFsHeader(index); byte[] key = GetContentKey(NcaKeyType.AesCtr); byte[] counter = Aes128CtrStorage.CreateCounter(fsHeader.Counter, Header.GetSectionStartOffset(index)); var aesStorage = new Aes128CtrStorage(baseStorage, key, Header.GetSectionStartOffset(index), counter, true); return(new CachedStorage(aesStorage, 0x4000, 4, true)); }
public IStorage OpenStorage(int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorage(index); NcaFsHeader header = GetFsHeader(index); if (header.EncryptionType == NcaEncryptionType.AesCtrEx) { return(rawStorage.Slice(0, header.GetPatchInfo().RelocationTreeOffset)); } return(CreateVerificationStorage(integrityCheckLevel, header, rawStorage)); }
internal void GenerateAesCounter(int sectionIndex, Ncm.ContentType type, int minorVersion) { int counterType; int counterVersion; NcaFsHeader header = Header.GetFsHeader(sectionIndex); if (header.EncryptionType != NcaEncryptionType.AesCtr && header.EncryptionType != NcaEncryptionType.AesCtrEx) { return; } switch (type) { case Ncm.ContentType.Program: counterType = sectionIndex + 1; break; case Ncm.ContentType.HtmlDocument: counterType = (int)Ncm.ContentType.HtmlDocument; break; case Ncm.ContentType.LegalInformation: counterType = (int)Ncm.ContentType.LegalInformation; break; default: counterType = 0; break; } // Version of firmware NCAs appears to always be 0 // Haven't checked delta fragment NCAs switch (Header.ContentType) { case NcaContentType.Program: case NcaContentType.Manual: counterVersion = Math.Max(minorVersion - 1, 0); break; case NcaContentType.PublicData: counterVersion = minorVersion << 16; break; default: counterVersion = 0; break; } header.CounterType = counterType; header.CounterVersion = counterVersion; }
private IFileSystem OpenFileSystem(IStorage storage, NcaFsHeader header) { switch (header.FormatType) { case NcaFormatType.Pfs0: return(new PartitionFileSystem(storage)); case NcaFormatType.Romfs: return(new RomFsFileSystem(storage)); default: throw new ArgumentOutOfRangeException(); } }
private IStorage OpenAesCtrExStorage(IStorage baseStorage, int index, bool decrypting) { NcaFsHeader fsHeader = GetFsHeader(index); NcaFsPatchInfo info = fsHeader.GetPatchInfo(); long sectionOffset = Header.GetSectionStartOffset(index); long sectionSize = Header.GetSectionSize(index); long bktrOffset = info.RelocationTreeOffset; long bktrSize = sectionSize - bktrOffset; long dataSize = info.RelocationTreeOffset; byte[] key = GetContentKey(NcaKeyType.AesCtr); byte[] counter = Aes128CtrStorage.CreateCounter(fsHeader.Counter, bktrOffset + sectionOffset); byte[] counterEx = Aes128CtrStorage.CreateCounter(fsHeader.Counter, sectionOffset); IStorage bucketTreeData; IStorage outputBucketTreeData; if (decrypting) { bucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true); outputBucketTreeData = bucketTreeData; } else { bucketTreeData = baseStorage.Slice(bktrOffset, bktrSize); outputBucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true); } var encryptionBucketTreeData = new SubStorage(bucketTreeData, info.EncryptionTreeOffset - bktrOffset, sectionSize - info.EncryptionTreeOffset); var cachedBucketTreeData = new CachedStorage(encryptionBucketTreeData, IndirectStorage.NodeSize, 6, true); var treeHeader = new BucketTree.Header(); info.EncryptionTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount); var tableNodeStorage = new SubStorage(cachedBucketTreeData, 0, nodeStorageSize); var tableEntryStorage = new SubStorage(cachedBucketTreeData, nodeStorageSize, entryStorageSize); IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), tableNodeStorage, tableEntryStorage, treeHeader.EntryCount, key, counterEx, true); return(new ConcatenationStorage(new[] { decStorage, outputBucketTreeData }, true)); }
public IStorage OpenRawStorageWithPatch(Nca patchNca, int index) { IStorage patchStorage = patchNca.OpenRawStorage(index); IStorage baseStorage = SectionExists(index) ? OpenRawStorage(index) : new NullStorage(); NcaFsHeader header = patchNca.Header.GetFsHeader(index); NcaFsPatchInfo patchInfo = header.GetPatchInfo(); if (patchInfo.RelocationTreeSize == 0) { return(patchStorage); } IStorage relocationTableStorage = patchStorage.Slice(patchInfo.RelocationTreeOffset, patchInfo.RelocationTreeSize); return(new IndirectStorage(relocationTableStorage, true, baseStorage, patchStorage)); }
public IStorage OpenStorageWithPatch(Nca patchNca, int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorageWithPatch(patchNca, index); NcaFsHeader header = patchNca.Header.GetFsHeader(index); switch (header.HashType) { case NcaHashType.Sha256: return(InitIvfcForPartitionFs(header.GetIntegrityInfoSha256(), rawStorage, integrityCheckLevel, true)); case NcaHashType.Ivfc: return(InitIvfcForRomFs(header.GetIntegrityInfoIvfc(), rawStorage, integrityCheckLevel, true)); default: throw new ArgumentOutOfRangeException(); } }
public IStorage OpenStorage(int index, IntegrityCheckLevel integrityCheckLevel) { IStorage rawStorage = OpenRawStorage(index); NcaFsHeader header = Header.GetFsHeader(index); if (header.EncryptionType == NcaEncryptionType.AesCtrEx) { return(rawStorage.Slice(0, header.GetPatchInfo().RelocationTreeOffset)); } switch (header.HashType) { case NcaHashType.Sha256: return(InitIvfcForPartitionFs(header.GetIntegrityInfoSha256(), rawStorage, integrityCheckLevel, true)); case NcaHashType.Ivfc: return(InitIvfcForRomFs(header.GetIntegrityInfoIvfc(), rawStorage, integrityCheckLevel, true)); default: throw new ArgumentOutOfRangeException(); } }
private IStorage OpenDecryptedStorage(IStorage baseStorage, int index) { NcaFsHeader header = Header.GetFsHeader(index); switch (header.EncryptionType) { case NcaEncryptionType.None: return(baseStorage); case NcaEncryptionType.XTS: return(OpenAesXtsStorage(baseStorage, index)); case NcaEncryptionType.AesCtr: return(OpenAesCtrStorage(baseStorage, index)); case NcaEncryptionType.AesCtrEx: return(OpenAesCtrExStorage(baseStorage, index)); default: throw new ArgumentOutOfRangeException(); } }
public IStorage OpenRawStorageWithPatch(Nca patchNca, int index) { IStorage patchStorage = patchNca.OpenRawStorage(index); IStorage baseStorage = SectionExists(index) ? OpenRawStorage(index) : new NullStorage(); patchStorage.GetSize(out long patchSize).ThrowIfFailure(); baseStorage.GetSize(out long baseSize).ThrowIfFailure(); NcaFsHeader header = patchNca.Header.GetFsHeader(index); NcaFsPatchInfo patchInfo = header.GetPatchInfo(); if (patchInfo.RelocationTreeSize == 0) { return(patchStorage); } var treeHeader = new BucketTree.Header(); patchInfo.RelocationTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount); var relocationTableStorage = new SubStorage(patchStorage, patchInfo.RelocationTreeOffset, patchInfo.RelocationTreeSize); var cachedTableStorage = new CachedStorage(relocationTableStorage, IndirectStorage.NodeSize, 4, true); var tableNodeStorage = new SubStorage(cachedTableStorage, 0, nodeStorageSize); var tableEntryStorage = new SubStorage(cachedTableStorage, nodeStorageSize, entryStorageSize); var storage = new IndirectStorage(); storage.Initialize(tableNodeStorage, tableEntryStorage, treeHeader.EntryCount).ThrowIfFailure(); storage.SetStorage(0, baseStorage, 0, baseSize); storage.SetStorage(1, patchStorage, 0, patchSize); return(storage); }
private IStorage CreateVerificationStorage(IntegrityCheckLevel integrityCheckLevel, NcaFsHeader header, IStorage rawStorage) { switch (header.HashType) { case NcaHashType.Sha256: return(InitIvfcForPartitionFs(header.GetIntegrityInfoSha256(), rawStorage, integrityCheckLevel, true)); case NcaHashType.Ivfc: // The FS header of an NCA0 section with IVFC verification must be manually skipped if (Header.IsNca0()) { rawStorage = rawStorage.Slice(0x200); } return(InitIvfcForRomFs(header.GetIntegrityInfoIvfc(), rawStorage, integrityCheckLevel, true)); default: throw new ArgumentOutOfRangeException(); } }
public static Validity ValidateSectionMasterHash(this Nca nca, int index) { if (!nca.SectionExists(index)) { throw new ArgumentException(nameof(index), Messages.NcaSectionMissing); } if (!nca.CanOpenSection(index)) { return(Validity.MissingKey); } NcaFsHeader header = nca.Header.GetFsHeader(index); // The base data is needed to validate the hash, so use a trick involving the AES-CTR extended // encryption table to check if the decryption is invalid. // todo: If the patch replaces the data checked by the master hash, use that directly if (header.IsPatchSection()) { if (header.EncryptionType != NcaEncryptionType.AesCtrEx) { return(Validity.Unchecked); } Validity ctrExValidity = ValidateCtrExDecryption(nca, index); return(ctrExValidity == Validity.Invalid ? Validity.Invalid : Validity.Unchecked); } byte[] expectedHash; long offset; long size; switch (header.HashType) { case NcaHashType.Ivfc: NcaFsIntegrityInfoIvfc ivfcInfo = header.GetIntegrityInfoIvfc(); expectedHash = ivfcInfo.MasterHash.ToArray(); offset = ivfcInfo.GetLevelOffset(0); size = 1 << ivfcInfo.GetLevelBlockSize(0); break; case NcaHashType.Sha256: NcaFsIntegrityInfoSha256 sha256Info = header.GetIntegrityInfoSha256(); expectedHash = sha256Info.MasterHash.ToArray(); offset = sha256Info.GetLevelOffset(0); size = sha256Info.GetLevelSize(0); break; default: return(Validity.Unchecked); } IStorage storage = nca.OpenRawStorage(index); var data = new byte[size]; storage.Read(offset, data).ThrowIfFailure(); var actualHash = new byte[Sha256.DigestSize]; Sha256.GenerateSha256Hash(data, actualHash); if (Utilities.ArraysEqual(expectedHash, actualHash)) { return(Validity.Valid); } return(Validity.Invalid); }