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)); }
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); }
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); }
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; } }
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); }