Esempio n. 1
0
 public static IStorage OpenStorage(this Nca nca, NcaSectionType type, IntegrityCheckLevel integrityCheckLevel,
                                    bool openRaw)
 {
     if (openRaw)
     {
         return(nca.OpenRawStorage(type));
     }
     return(nca.OpenStorage(type, integrityCheckLevel));
 }
Esempio n. 2
0
 public static IStorage OpenStorage(this Nca nca, int index, IntegrityCheckLevel integrityCheckLevel,
                                    bool openRaw)
 {
     if (openRaw)
     {
         return(nca.OpenRawStorage(index));
     }
     return(nca.OpenStorage(index, integrityCheckLevel));
 }
Esempio n. 3
0
        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));
        }
Esempio n. 4
0
        private static Validity ValidateCtrExDecryption(Nca nca, int index)
        {
            // The encryption subsection table in an AesCtrEx-encrypted partition contains the length of the entire partition.
            // The encryption table is always located immediately following the partition data, so the offset value of the encryption
            // table located in the NCA header should be the same as the size read from the encryption table.

            Debug.Assert(nca.CanOpenSection(index));

            NcaFsPatchInfo header           = nca.Header.GetFsHeader(index).GetPatchInfo();
            IStorage       decryptedStorage = nca.OpenRawStorage(index);

            Span <byte> buffer = stackalloc byte[sizeof(long)];

            decryptedStorage.Read(header.EncryptionTreeOffset + 8, buffer).ThrowIfFailure();
            long readDataSize = BinaryPrimitives.ReadInt64LittleEndian(buffer);

            if (header.EncryptionTreeOffset != readDataSize)
            {
                return(Validity.Invalid);
            }

            return(Validity.Valid);
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
        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);
        }