Exemple #1
0
        public static IStorage ProcessXci(IStorage xciStorage, bool compress)
        {
            Xci      xci = new Xci(KeySet, xciStorage);
            IStorage xciHeaderStorage = new MemoryStorage(new byte[0x200]);

            xciStorage.CopyTo(xciHeaderStorage);

            XciPartition securePartition          = xci.OpenPartition(XciPartitionType.Secure);
            IStorage     processedSecurePartition = ProcessPartitionFileSystem(securePartition, PartitionFileSystemType.Hashed, compress);

            PartitionFileSystemBuilder rootPartitionBuilder = new PartitionFileSystemBuilder();

            rootPartitionBuilder.AddFile("secure", processedSecurePartition.AsFile(OpenMode.Read));
            IStorage rootPartitionStorage = rootPartitionBuilder.Build(PartitionFileSystemType.Hashed);

            using (BinaryReader reader = new BinaryReader(rootPartitionStorage.AsStream()))
            {
                PartitionFileSystemHeader pfsHeader = new PartitionFileSystemHeader(reader);
                Span <byte> pfsHeaderBytes          = stackalloc byte[pfsHeader.HeaderSize];
                Span <byte> pfsHeaderHash           = stackalloc byte[0x20];
                rootPartitionStorage.Read(0, pfsHeaderBytes);
                Sha256.GenerateSha256Hash(pfsHeaderBytes, pfsHeaderHash);

                xciHeaderStorage.Write(0x138, BitConverter.GetBytes(pfsHeader.HeaderSize));
                xciHeaderStorage.Write(0x140, pfsHeaderHash);
            }

            return(new ConcatenationStorage(new[]
            {
                xciHeaderStorage,
                new MemoryStorage(new byte[xci.Header.RootPartitionOffset - 0x200]), // Cert and padding
                rootPartitionStorage
            }, false));
        }
Exemple #2
0
        public Header(IStorage storage, KeySet keySet)
        {
            MainStorage         = storage;
            MainHeader          = MainStorage.Slice(0x100, 0x200);
            DuplexHeader        = MainStorage.Slice(0x300, 0x44);
            DataIvfcHeader      = MainStorage.Slice(0x344, 0xC0);
            JournalHeader       = MainStorage.Slice(0x408, 0x200);
            SaveHeader          = MainStorage.Slice(0x608, 0x48);
            MainRemapHeader     = MainStorage.Slice(0x650, 0x40);
            MetaDataRemapHeader = MainStorage.Slice(0x690, 0x40);
            ExtraDataStorage    = MainStorage.Slice(0x6D8, 0x400);
            FatIvfcHeader       = MainStorage.Slice(0xAD8, 0xC0);

            Layout = new FsLayout(MainHeader);

            DuplexMasterBitmapA = MainStorage.Slice(Layout.DuplexMasterOffsetA, Layout.DuplexMasterSize);
            DuplexMasterBitmapB = MainStorage.Slice(Layout.DuplexMasterOffsetB, Layout.DuplexMasterSize);
            DataIvfcMaster      = MainStorage.Slice(Layout.IvfcMasterHashOffsetA, Layout.IvfcMasterHashSize);
            FatIvfcMaster       = MainStorage.Slice(Layout.FatIvfcMasterHashA, Layout.IvfcMasterHashSize);

            var reader = new BinaryReader(storage.AsStream());

            reader.BaseStream.Position = 0;
            Data = reader.ReadBytes(0x4000);
            reader.BaseStream.Position = 0;

            Cmac = reader.ReadBytes(0x10);

            reader.BaseStream.Position = 0x100;

            reader.BaseStream.Position = 0x300;
            Duplex = new DuplexHeader(reader);

            reader.BaseStream.Position = 0x6D8;
            ExtraData = new ExtraData(reader);

            Ivfc = new IvfcHeader(DataIvfcHeader)
            {
                NumLevels = 5
            };

            if (Layout.Version >= 0x50000)
            {
                FatIvfc = new IvfcHeader(FatIvfcHeader)
                {
                    NumLevels = 4
                };
            }

            MasterHash = storage.Slice(Layout.IvfcMasterHashOffsetA, Layout.IvfcMasterHashSize);

            Span <byte> actualHeaderHash = stackalloc byte[Sha256.DigestSize];

            Sha256.GenerateSha256Hash(Data.AsSpan(0x300, 0x3d00), actualHeaderHash);

            HeaderHashValidity = Utilities.SpansEqual(Layout.Hash, actualHeaderHash) ? Validity.Valid : Validity.Invalid;
            SignatureValidity  = ValidateSignature(keySet);
        }
        public Result Commit(Keyset keyset)
        {
            CoreDataIvfcStorage.Flush();
            FatIvfcStorage?.Flush();

            Stream headerStream = BaseStorage.AsStream();

            var hashData = new byte[0x3d00];

            headerStream.Position = 0x300;
            headerStream.Read(hashData, 0, hashData.Length);

            var hash = new byte[Sha256.DigestSize];

            Sha256.GenerateSha256Hash(hashData, hash);

            headerStream.Position = 0x108;
            headerStream.Write(hash, 0, hash.Length);

            if (keyset == null || keyset.SaveMacKey.IsEmpty())
            {
                return(ResultFs.PreconditionViolation);
            }

            var cmacData = new byte[0x200];
            var cmac     = new byte[0x10];

            headerStream.Position = 0x100;
            headerStream.Read(cmacData, 0, 0x200);

            CryptoOld.CalculateAesCmac(keyset.SaveMacKey, cmacData, 0, cmac, 0, 0x200);

            headerStream.Position = 0;
            headerStream.Write(cmac, 0, 0x10);
            headerStream.Flush();

            return(Result.Success);
        }
Exemple #4
0
        public Result Commit(KeySet keySet)
        {
            CoreDataIvfcStorage.Flush();
            FatIvfcStorage?.Flush();

            Stream headerStream = BaseStorage.AsStream();

            var hashData = new byte[0x3d00];

            headerStream.Position = 0x300;
            headerStream.Read(hashData, 0, hashData.Length);

            var hash = new byte[Sha256.DigestSize];

            Sha256.GenerateSha256Hash(hashData, hash);

            headerStream.Position = 0x108;
            headerStream.Write(hash, 0, hash.Length);

            if (keySet == null || keySet.DeviceUniqueSaveMacKeys[0].IsZeros())
            {
                return(ResultFs.PreconditionViolation.Log());
            }

            var cmacData = new byte[0x200];
            var cmac     = new byte[0x10];

            headerStream.Position = 0x100;
            headerStream.Read(cmacData, 0, 0x200);

            Aes.CalculateCmac(cmac, cmacData, keySet.DeviceUniqueSaveMacKeys[0]);

            headerStream.Position = 0;
            headerStream.Write(cmac, 0, 0x10);
            headerStream.Flush();

            return(Result.Success);
        }
Exemple #5
0
        public XciHeader(KeySet keySet, Stream stream)
        {
            using (var reader = new BinaryReader(stream, Encoding.Default, true))
            {
                Signature = reader.ReadBytes(SignatureSize);
                Magic     = reader.ReadAscii(4);
                if (Magic != HeaderMagic)
                {
                    throw new InvalidDataException("Invalid XCI file: Header magic invalid.");
                }

                reader.BaseStream.Position = SignatureSize;
                byte[] sigData = reader.ReadBytes(SignatureSize);
                reader.BaseStream.Position = SignatureSize + 4;

                SignatureValidity = CryptoOld.Rsa2048Pkcs1Verify(sigData, Signature, _xciHeaderPubk);

                RomAreaStartPage    = reader.ReadInt32();
                BackupAreaStartPage = reader.ReadInt32();
                byte keyIndex = reader.ReadByte();
                KekIndex          = (byte)(keyIndex >> 4);
                TitleKeyDecIndex  = (byte)(keyIndex & 7);
                GameCardSize      = (GameCardSizeInternal)reader.ReadByte();
                CardHeaderVersion = reader.ReadByte();
                Flags             = (GameCardAttribute)reader.ReadByte();
                PackageId         = reader.ReadUInt64();
                ValidDataEndPage  = reader.ReadInt64();
                AesCbcIv          = reader.ReadBytes(Aes.KeySize128);
                Array.Reverse(AesCbcIv);
                RootPartitionOffset     = reader.ReadInt64();
                RootPartitionHeaderSize = reader.ReadInt64();
                RootPartitionHeaderHash = reader.ReadBytes(Sha256.DigestSize);
                InitialDataHash         = reader.ReadBytes(Sha256.DigestSize);
                SelSec      = reader.ReadInt32();
                SelT1Key    = reader.ReadInt32();
                SelKey      = reader.ReadInt32();
                LimAreaPage = reader.ReadInt32();

                if (keySet != null && !keySet.XciHeaderKey.IsZeros())
                {
                    byte[] encHeader = reader.ReadBytes(EncryptedHeaderSize);
                    byte[] decHeader = new byte[EncryptedHeaderSize];
                    Aes.DecryptCbc128(encHeader, decHeader, keySet.XciHeaderKey, AesCbcIv);

                    using (var decReader = new BinaryReader(new MemoryStream(decHeader)))
                    {
                        FwVersion      = decReader.ReadUInt64();
                        AccCtrl1       = (CardClockRate)decReader.ReadInt32();
                        Wait1TimeRead  = decReader.ReadInt32();
                        Wait2TimeRead  = decReader.ReadInt32();
                        Wait1TimeWrite = decReader.ReadInt32();
                        Wait2TimeWrite = decReader.ReadInt32();
                        FwMode         = decReader.ReadInt32();
                        UppVersion     = decReader.ReadInt32();
                        decReader.BaseStream.Position += 4;
                        UppHash = decReader.ReadBytes(8);
                        UppId   = decReader.ReadUInt64();
                    }
                }

                ImageHash = new byte[Sha256.DigestSize];
                Sha256.GenerateSha256Hash(sigData, ImageHash);

                reader.BaseStream.Position = RootPartitionOffset;
                byte[] headerBytes = reader.ReadBytes((int)RootPartitionHeaderSize);

                Span <byte> actualHeaderHash = stackalloc byte[Sha256.DigestSize];
                Sha256.GenerateSha256Hash(headerBytes, actualHeaderHash);

                PartitionFsHeaderValidity = Utilities.SpansEqual(RootPartitionHeaderHash, actualHeaderHash) ? Validity.Valid : Validity.Invalid;
            }
        }
Exemple #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);
        }