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); }
public NcaHeader(BinaryReader reader, Keyset keyset) { Signature1 = reader.ReadBytes(0x100); Signature2 = reader.ReadBytes(0x100); Magic = reader.ReadAscii(4); if (!Magic.StartsWith("NCA") || Magic[3] < '0' || Magic[3] > '9') { throw new InvalidDataException("Unable to decrypt NCA header, or the file is not an NCA file."); } Version = Magic[3] - '0'; if (Version != 2 && Version != 3) { throw new NotSupportedException($"NCA version {Version} is not supported."); } reader.BaseStream.Position -= 4; SignatureData = reader.ReadBytes(0x200); FixedSigValidity = Crypto.Rsa2048PssVerify(SignatureData, Signature1, keyset.NcaHdrFixedKeyModulus); reader.BaseStream.Position -= 0x200 - 4; Distribution = (DistributionType)reader.ReadByte(); ContentType = (ContentType)reader.ReadByte(); CryptoType = reader.ReadByte(); KaekInd = reader.ReadByte(); NcaSize = reader.ReadInt64(); TitleId = reader.ReadUInt64(); reader.BaseStream.Position += 4; SdkVersion = new TitleVersion(reader.ReadUInt32()); CryptoType2 = reader.ReadByte(); reader.BaseStream.Position += 0xF; RightsId = reader.ReadBytes(0x10); for (int i = 0; i < 4; i++) { SectionEntries[i] = new NcaSectionEntry(reader); } for (int i = 0; i < 4; i++) { SectionHashes[i] = reader.ReadBytes(0x20); } for (int i = 0; i < 4; i++) { EncryptedKeys[i] = reader.ReadBytes(0x10); } reader.BaseStream.Position += 0xC0; for (int i = 0; i < 4; i++) { FsHeaders[i] = new NcaFsHeader(reader); } }
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(data, offset); byte[] actualHash = Crypto.ComputeSha256(data, 0, data.Length); if (Util.ArraysEqual(expectedHash, actualHash)) { return(Validity.Valid); } return(Validity.Invalid); }