/// <summary> /// Decrypts the given entry using the entry encryption. /// Throws an exception if it can't be decrypted. /// </summary> public static byte[] Decrypt(byte[] entryBytes, Pkg pkg, MetaEntry meta) { if (meta.KeyIndex != 3) { throw new Exception("We only have the key for encryption key 3"); } return(Decrypt(entryBytes, Crypto.RSA2048Decrypt(pkg.EntryKeys.Keys[3].key, RSAKeyset.PkgDerivedKey3Keyset), meta)); }
private static byte[] Decrypt(byte[] entryBytes, byte[] keySeed, MetaEntry meta) { var iv_key = Crypto.Sha256( meta.GetBytes() .Concat(keySeed) .ToArray()); var tmp = new byte[entryBytes.Length]; Crypto.AesCbcCfb128Decrypt(tmp, entryBytes, tmp.Length, iv_key.Skip(16).Take(16).ToArray(), iv_key.Take(16).ToArray()); return(tmp); }
public Pkg ReadPkg() { var header = ReadHeader(); s.Position = 0xFE0; var headerDigest = s.ReadBytes(32); var headerSignature = s.ReadBytes(256); s.Position = header.entry_table_offset; var metasEntry = new MetasEntry(); for (var i = 0; i < header.entry_count; i++) { metasEntry.Metas.Add(MetaEntry.Read(s)); } var pkg = new Pkg { Header = header, HeaderDigest = headerDigest, HeaderSignature = headerSignature, Metas = metasEntry, }; foreach (var entry in pkg.Metas.Metas) { switch (entry.id) { case EntryId.PARAM_SFO: s.Position = entry.DataOffset; pkg.ParamSfo = new SfoEntry(SFO.ParamSfo.FromStream(s)); break; case EntryId.ENTRY_KEYS: pkg.EntryKeys = KeysEntry.Read(entry, s); break; case EntryId.IMAGE_KEY: s.Position = entry.DataOffset; pkg.ImageKey = new GenericEntry(EntryId.IMAGE_KEY) { FileData = s.ReadBytes((int)entry.DataSize), meta = entry }; break; case EntryId.GENERAL_DIGESTS: s.Position = entry.DataOffset; pkg.GeneralDigests = GeneralDigestsEntry.Read(s); break; } } return(pkg); }
public static MetaEntry Read(Stream s) { var ret = new MetaEntry(); ret.id = (EntryId)s.ReadUInt32BE(); ret.NameTableOffset = s.ReadUInt32BE(); ret.Flags1 = s.ReadUInt32BE(); ret.Flags2 = s.ReadUInt32BE(); ret.DataOffset = s.ReadUInt32BE(); ret.DataSize = s.ReadUInt32BE(); s.Position += 8; return(ret); }
/// <summary> /// Decrypts the given entry using the entry encryption. /// Throws an exception if it can't be decrypted. /// </summary> public static byte[] Decrypt(byte[] entryBytes, Pkg pkg, MetaEntry meta) { if (meta.KeyIndex != 3) { throw new Exception("We only have the key for encryption key 3"); } var iv_key = Crypto.Sha256( meta.GetBytes() .Concat(Crypto.RSA2048Decrypt(pkg.EntryKeys.Keys[3].key, RSAKeyset.PkgDerivedKey3Keyset)) .ToArray()); var tmp = new byte[entryBytes.Length]; Crypto.AesCbcCfb128Decrypt(tmp, entryBytes, tmp.Length, iv_key.Skip(16).Take(16).ToArray(), iv_key.Take(16).ToArray()); return(tmp); }
public static NameTableEntry Read(MetaEntry e, Stream pkg) { var sz = 0; var names = new List <string>(); pkg.Position = e.DataOffset; while (sz < e.DataSize) { var name = pkg.ReadASCIINullTerminated((int)e.DataSize); names.Add(name); sz += name.Length + 1; } return(new NameTableEntry(names) { meta = e }); }
public Pkg ReadPkg() { var header = ReadHeader(); s.Position = 0xFE0; var headerDigest = s.ReadBytes(32); var headerSignature = s.ReadBytes(256); s.Position = header.entry_table_offset; var metasEntry = new MetasEntry(); for (var i = 0; i < header.entry_count; i++) { metasEntry.Metas.Add(MetaEntry.Read(s)); } return(new Pkg { Header = header, HeaderDigest = headerDigest, HeaderSignature = headerSignature, Metas = metasEntry, }); }
public static KeysEntry Read(MetaEntry e, Stream pkg) { pkg.Position = e.DataOffset; var seedDigest = pkg.ReadBytes(32); var digests = new byte[7][]; var keys = new PkgEntryKey[7]; for (var x = 0; x < 7; x++) { digests[x] = pkg.ReadBytes(32); } for (var x = 0; x < 7; x++) { keys[x] = new PkgEntryKey { digest = digests[x], key = pkg.ReadBytes(256) }; } return(new KeysEntry(seedDigest, keys) { meta = e }); }
public Pkg BuildPkg() { var pkg = new Pkg(); var volType = GP4.VolumeTypeUtil.OfString(project.volume.Type); pkg.Header = new Header { CNTMagic = "\u007fCNT", flags = PKGFlags.Unknown, unk_0x08 = 0, unk_0x0C = 0xF, entry_count = 6, sc_entry_count = 6, entry_count_2 = 6, entry_table_offset = 0x2A80, main_ent_data_size = 0xD00, body_offset = 0x2000, body_size = 0x7E000, content_id = project.volume.Package.ContentId, drm_type = DrmType.PS4, content_type = VolTypeToContentType(volType), content_flags = ContentFlags.Unk_x8000000 | VolTypeToContentFlags(volType), // TODO promote_size = 0, version_date = 0x20161020, version_hash = 0x1738551, unk_0x88 = 0, unk_0x8C = 0, unk_0x90 = 0, unk_0x94 = 0, iro_tag = IROTag.None, ekc_version = 1, sc_entries1_hash = new byte[32], sc_entries2_hash = new byte[32], digest_table_hash = new byte[32], body_digest = new byte[32], unk_0x400 = 1, pfs_image_count = 1, pfs_flags = 0x80000000000003CC, pfs_image_offset = 0x80000, pfs_image_size = 0, mount_image_offset = 0, mount_image_size = 0, package_size = 0, pfs_signed_size = 0x10000, pfs_cache_size = 0x90000, pfs_image_digest = new byte[32], pfs_signed_digest = new byte[32], pfs_split_size_nth_0 = 0, pfs_split_size_nth_1 = 0 }; pkg.PackageDigest = new byte[32]; pkg.UnkKey = new byte[0x100]; pkg.EntryKeys = new KeysEntry(); pkg.ImageKey = new GenericEntry(EntryId.IMAGE_KEY) { FileData = new byte[0x100] }; pkg.GeneralDigests = new GeneralDigestsEntry() { UnknownDigest = new byte[] { 0xD2, 0x56, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE }, ContentDigest = new byte[32], GameDigest = new byte[32], HeaderDigest = new byte[32], UnknownDigest2 = new byte[32], MajorParamDigest = new byte[32], ParamDigest = new byte[32], }; pkg.Metas = new MetasEntry(); pkg.Digests = new GenericEntry(EntryId.DIGESTS); pkg.EntryNames = new NameTableEntry(); pkg.LicenseDat = new GenericEntry(EntryId.LICENSE_DAT) { FileData = new byte[1024] }; pkg.LicenseInfo = new GenericEntry(EntryId.LICENSE_INFO) { FileData = new byte[512] }; var paramSfoPath = project.files.Where(f => f.TargetPath == "sce_sys/param.sfo").First().OrigPath; using (var paramSfo = File.OpenRead(Path.Combine(projectDir, paramSfoPath))) pkg.ParamSfo = new GenericEntry(EntryId.PARAM_SFO, "param.sfo") { FileData = SFO.ParamSfo.Update(paramSfo) }; pkg.PsReservedDat = new GenericEntry(EntryId.PSRESERVED_DAT) { FileData = new byte[0x2000] }; pkg.Entries = new List <Entry> { pkg.EntryKeys, pkg.ImageKey, pkg.GeneralDigests, pkg.Metas, pkg.Digests, pkg.EntryNames, pkg.LicenseDat, pkg.LicenseInfo, pkg.ParamSfo, pkg.PsReservedDat }; pkg.Digests.FileData = new byte[pkg.Entries.Count * Pkg.HASH_SIZE]; // 1st pass: set names foreach (var entry in pkg.Entries) { pkg.EntryNames.GetOffset(entry.Name); } // 2nd pass: set sizes, offsets in meta table var dataOffset = 0x2000u; foreach (var entry in pkg.Entries) { var e = new MetaEntry { id = entry.Id, NameTableOffset = pkg.EntryNames.GetOffset(entry.Name), DataOffset = dataOffset, DataSize = entry.Length, // TODO Flags1 = 0, Flags2 = 0, }; pkg.Metas.Metas.Add(e); if (entry == pkg.Metas) { e.DataSize = (uint)pkg.Entries.Count * 32; } dataOffset += e.DataSize; var align = dataOffset % 16; if (align != 0) { dataOffset += 16 - align; } } pkg.Metas.Metas.Sort((e1, e2) => e1.id.CompareTo(e2.id)); pkg.Header.entry_count = (uint)pkg.Entries.Count; pkg.Header.entry_count_2 = (ushort)pkg.Entries.Count; pkg.Header.body_size = dataOffset; // TODO: mount_image_size, package_size return(pkg); }
public Pkg ReadPkg() { var header = ReadHeader(); s.Position = 0xFE0; var headerDigest = s.ReadBytes(32); var headerSignature = s.ReadBytes(256); s.Position = header.entry_table_offset; var metasEntry = new MetasEntry(); for (var i = 0; i < header.entry_count; i++) { metasEntry.Metas.Add(MetaEntry.Read(s)); } var pkg = new Pkg { Header = header, HeaderDigest = headerDigest, HeaderSignature = headerSignature, Metas = metasEntry, }; foreach (var entry in pkg.Metas.Metas) { switch (entry.id) { case EntryId.METAS: pkg.Metas.meta = entry; break; case EntryId.ENTRY_NAMES: pkg.EntryNames = NameTableEntry.Read(entry, s); break; case EntryId.PARAM_SFO: s.Position = entry.DataOffset; pkg.ParamSfo = new SfoEntry(SFO.ParamSfo.FromStream(s)); pkg.ParamSfo.meta = entry; break; case EntryId.ENTRY_KEYS: pkg.EntryKeys = KeysEntry.Read(entry, s); pkg.EntryKeys.meta = entry; break; case EntryId.IMAGE_KEY: s.Position = entry.DataOffset; pkg.ImageKey = new GenericEntry(EntryId.IMAGE_KEY) { FileData = s.ReadBytes((int)entry.DataSize), meta = entry }; break; case EntryId.GENERAL_DIGESTS: s.Position = entry.DataOffset; pkg.GeneralDigests = GeneralDigestsEntry.Read(s); pkg.GeneralDigests.meta = entry; break; case EntryId.DIGESTS: s.Position = entry.DataOffset; pkg.Digests = new GenericEntry(EntryId.DIGESTS) { FileData = s.ReadBytes((int)entry.DataSize), meta = entry, }; break; case EntryId.LICENSE_DAT: try { var licenseDatBytes = new byte[entry.DataSize]; s.Position = entry.DataOffset; s.Read(licenseDatBytes, 0, (int)entry.DataSize); using (var ms = new System.IO.MemoryStream(Entry.Decrypt(licenseDatBytes, pkg, entry))) { pkg.LicenseDat = new Rif.LicenseDatReader(ms).Read(); pkg.LicenseDat.meta = entry; } } catch (Exception) { } break; } } return(pkg); }
/// <summary> /// Creates the Pkg object. Initializes the header and body. /// </summary> public void BuildPkg(long pfsSize) { pkg = new Pkg(); var volType = project.VolumeType; pkg.Header = new Header { CNTMagic = "\u007fCNT", flags = PKGFlags.Unknown, unk_0x08 = 0, unk_0x0C = 0xF, entry_count = 6, sc_entry_count = 6, entry_count_2 = 6, entry_table_offset = 0x2A80, main_ent_data_size = 0xD00, body_offset = 0x2000, body_size = 0x7E000, content_id = project.ContentId, drm_type = DrmType.PS4, content_type = VolTypeToContentType(volType), content_flags = ContentFlags.Unk_x8000000 | VolTypeToContentFlags(volType), // TODO promote_size = 0, version_date = 0x20161020, version_hash = 0x1738551, unk_0x88 = 0, unk_0x8C = 0, unk_0x90 = 0, unk_0x94 = 0, iro_tag = IROTag.None, ekc_version = 1, sc_entries1_hash = new byte[32], sc_entries2_hash = new byte[32], digest_table_hash = new byte[32], body_digest = new byte[32], unk_0x400 = 1, pfs_image_count = 1, pfs_flags = 0x80000000000003CC, pfs_image_offset = 0x80000, pfs_image_size = (ulong)pfsSize, mount_image_offset = 0, mount_image_size = 0, package_size = (ulong)(0x80000 + pfsSize), pfs_signed_size = 0x10000, pfs_cache_size = 0xD0000, pfs_image_digest = new byte[32], pfs_signed_digest = new byte[32], pfs_split_size_nth_0 = 0, pfs_split_size_nth_1 = 0 }; pkg.HeaderDigest = new byte[32]; pkg.HeaderSignature = new byte[0x100]; pkg.EntryKeys = new KeysEntry( project.ContentId, project.Passcode); pkg.ImageKey = new GenericEntry(EntryId.IMAGE_KEY) { FileData = Crypto.RSA2048EncryptKey(RSAKeyset.FakeKeyset.Modulus, EKPFS) }; pkg.GeneralDigests = new GeneralDigestsEntry(); pkg.Metas = new MetasEntry(); pkg.Digests = new GenericEntry(EntryId.DIGESTS); pkg.EntryNames = new NameTableEntry(); pkg.LicenseDat = GenLicense(); pkg.LicenseInfo = new GenericEntry(EntryId.LICENSE_INFO) { FileData = GenLicenseInfo() }; var paramSfoFile = project.RootDir.GetFile("sce_sys/param.sfo"); if (paramSfoFile == null) { throw new Exception("Missing param.sfo!"); } using (var paramSfo = new MemoryStream()) { paramSfoFile.Write(paramSfo); paramSfo.Position = 0; var sfo = SFO.ParamSfo.FromStream(paramSfo); pkg.ParamSfo = new SfoEntry(sfo); string date = "", time = ""; if (project.CreationDate == default) { date = "c_date=" + DateTime.UtcNow.ToString("yyyyMMdd"); if (project.UseCreationTime) { time = ",c_time=" + DateTime.UtcNow.ToString("HHmmss"); } } else { date = "c_date=" + project.CreationDate.ToString("yyyyMMdd"); if (project.UseCreationTime) { time = ",c_time=" + project.CreationDate.ToString("HHmmss"); } } var sizeInfo = $",img0_l0_size={(pkg.Header.package_size + 0xFFFFF) / (1024 * 1024)}" + $",img0_l1_size=0" + $",img0_sc_ksize=512" + $",img0_pc_ksize=832"; sfo["PUBTOOLINFO"] = new SFO.Utf8Value("PUBTOOLINFO", date + time + sizeInfo, 0x200); sfo["PUBTOOLVER"] = new SFO.IntegerValue("PUBTOOLVER", 0x02890000); } pkg.PsReservedDat = new GenericEntry(EntryId.PSRESERVED_DAT) { FileData = new byte[0x2000] }; pkg.Entries = new List <Entry> { pkg.EntryKeys, pkg.ImageKey, pkg.GeneralDigests, pkg.Metas, pkg.Digests, pkg.EntryNames }; if (pkg.Header.content_type == ContentType.GD) { pkg.ChunkDat = PlayGo.ChunkDat.FromProject(project.ContentId); pkg.ChunkSha = new GenericEntry(EntryId.PLAYGO_CHUNK_SHA, "playgo-chunk.sha"); pkg.ChunkXml = new GenericEntry(EntryId.PLAYGO_MANIFEST_XML, "playgo-manifest.xml") { FileData = PlayGo.Manifest.Default }; // Add playgo entries for GD PKGs pkg.Entries.AddRange(new Entry[] { pkg.ChunkDat, pkg.ChunkSha, pkg.ChunkXml }); } pkg.Entries.AddRange(new Entry[] { pkg.LicenseDat, pkg.LicenseInfo, pkg.ParamSfo, pkg.PsReservedDat }); foreach (var file in project.RootDir.Dirs.Where(f => f.name == "sce_sys").First().Files.Where(f => EntryNames.NameToId.ContainsKey(f.name))) { var name = file.name; if (name == "param.sfo") { continue; } var entry = new FileEntry(EntryNames.NameToId[name], file.Write, (uint)file.Size); pkg.Entries.Add(entry); } pkg.Digests.FileData = new byte[pkg.Entries.Count * Pkg.HASH_SIZE]; // 1st pass: set names foreach (var entry in pkg.Entries.OrderBy(e => e.Name)) { pkg.EntryNames.GetOffset(entry.Name); } // estimate size for playgo if (pkg.Header.content_type == ContentType.GD) { long bodySize = 0; foreach (var e in pkg.Entries) { bodySize += (e.Length + 15) & ~15; // round up to nearest 16 } bodySize += 32 * pkg.Entries.Count; // metas bodySize += 4 * (pfsSize / 0x10000); // playgo hashes of pfs if (bodySize + (long)pkg.Header.body_offset >= (long)pkg.Header.pfs_image_offset) { pkg.Header.pfs_image_offset = (ulong)((bodySize + (long)pkg.Header.body_offset + 0xFFFF) & ~0xFFFFL); } pkg.ChunkSha.FileData = new byte[4 * ((pkg.Header.pfs_image_offset + pkg.Header.pfs_image_size) / 0x10000)]; } // 2nd pass: set sizes, offsets in meta table var dataOffset = 0x2000u; var flagMap = new Dictionary <EntryId, uint>() { { EntryId.DIGESTS, 0x40000000 }, { EntryId.ENTRY_KEYS, 0x60000000 }, { EntryId.IMAGE_KEY, 0xE0000000 }, { EntryId.GENERAL_DIGESTS, 0x60000000 }, { EntryId.METAS, 0x60000000 }, { EntryId.ENTRY_NAMES, 0x40000000 }, { EntryId.LICENSE_DAT, 0x80000000 }, { EntryId.LICENSE_INFO, 0x80000000 }, }; var keyMap = new Dictionary <EntryId, uint> { { EntryId.IMAGE_KEY, 3u << 12 }, { EntryId.LICENSE_DAT, 3u << 12 }, { EntryId.LICENSE_INFO, 2u << 12 }, }; foreach (var entry in pkg.Entries) { var e = new MetaEntry { id = entry.Id, NameTableOffset = pkg.EntryNames.GetOffset(entry.Name), DataOffset = dataOffset, DataSize = entry.Length, // TODO Flags1 = flagMap.GetOrDefault(entry.Id), Flags2 = keyMap.GetOrDefault(entry.Id), }; pkg.Metas.Metas.Add(e); if (entry == pkg.Metas) { e.DataSize = (uint)pkg.Entries.Count * 32; } dataOffset += e.DataSize; var align = dataOffset % 16; if (align != 0) { dataOffset += 16 - align; } entry.meta = e; } pkg.Metas.Metas.Sort((e1, e2) => e1.id.CompareTo(e2.id)); pkg.Header.entry_count = (uint)pkg.Entries.Count; pkg.Header.entry_count_2 = (ushort)pkg.Entries.Count; pkg.Header.body_size = pkg.Header.pfs_image_offset - pkg.Header.body_offset; pkg.Header.package_size = pkg.Header.mount_image_size = pkg.Header.pfs_image_offset + pkg.Header.pfs_image_size; if (pkg.Header.content_type == ContentType.GD) { // Important sizes for PlayGo ChunkDat pkg.ChunkDat.MchunkAttrs[0].size = pkg.Header.package_size; pkg.ChunkDat.InnerMChunkAttrs[0].size = (ulong)innerPfs.CalculatePfsSize(); // GD pkgs set promote_size to the size of the PKG before the PFS image? pkg.Header.promote_size = (uint)(pkg.Header.body_size + pkg.Header.body_offset); } }
/// <summary> /// Decrypts the given bytes using the entry encryption. /// </summary> public static byte[] Decrypt(byte[] entryBytes, string contentId, string passcode, MetaEntry meta) { var iv_key = Crypto.Sha256( meta.GetBytes() .Concat(Crypto.ComputeKeys(contentId, passcode, meta.KeyIndex)) .ToArray()); var tmp = new byte[entryBytes.Length]; Crypto.AesCbcCfb128Decrypt(tmp, entryBytes, tmp.Length, iv_key.Skip(16).Take(16).ToArray(), iv_key.Take(16).ToArray()); return(tmp); }
/// <summary> /// Create the Pkg struct. Does not compute hashes or data sizes. /// </summary> public Pkg BuildPkg() { var pkg = new Pkg(); var volType = GP4.VolumeTypeUtil.OfString(project.volume.Type); pkg.Header = new Header { CNTMagic = "\u007fCNT", flags = PKGFlags.Unknown, unk_0x08 = 0, unk_0x0C = 0xF, entry_count = 6, sc_entry_count = 6, entry_count_2 = 6, entry_table_offset = 0x2A80, main_ent_data_size = 0xD00, body_offset = 0x2000, body_size = 0x7E000, content_id = project.volume.Package.ContentId, drm_type = DrmType.PS4, content_type = VolTypeToContentType(volType), content_flags = ContentFlags.Unk_x8000000 | VolTypeToContentFlags(volType), // TODO promote_size = 0, version_date = 0x20161020, version_hash = 0x1738551, unk_0x88 = 0, unk_0x8C = 0, unk_0x90 = 0, unk_0x94 = 0, iro_tag = IROTag.None, ekc_version = 1, sc_entries1_hash = new byte[32], sc_entries2_hash = new byte[32], digest_table_hash = new byte[32], body_digest = new byte[32], unk_0x400 = 1, pfs_image_count = 1, pfs_flags = 0x80000000000003CC, pfs_image_offset = 0x80000, pfs_image_size = 0, mount_image_offset = 0, mount_image_size = 0, package_size = 0, pfs_signed_size = 0x10000, pfs_cache_size = 0xD0000, pfs_image_digest = new byte[32], pfs_signed_digest = new byte[32], pfs_split_size_nth_0 = 0, pfs_split_size_nth_1 = 0 }; pkg.HeaderDigest = new byte[32]; pkg.HeaderSignature = new byte[0x100]; pkg.EntryKeys = new KeysEntry( project.volume.Package.ContentId, project.volume.Package.Passcode); pkg.ImageKey = new GenericEntry(EntryId.IMAGE_KEY) { FileData = new byte[0x100] }; pkg.GeneralDigests = new GeneralDigestsEntry(); pkg.Metas = new MetasEntry(); pkg.Digests = new GenericEntry(EntryId.DIGESTS); pkg.EntryNames = new NameTableEntry(); pkg.LicenseDat = new GenericEntry(EntryId.LICENSE_DAT) { FileData = GenLicense(pkg) }; pkg.LicenseInfo = new GenericEntry(EntryId.LICENSE_INFO) { FileData = GenLicenseInfo(pkg) }; var paramSfoPath = project.files.Where(f => f.TargetPath == "sce_sys/param.sfo").First().OrigPath; using (var paramSfo = File.OpenRead(Path.Combine(projectDir, paramSfoPath))) { var sfo = SFO.ParamSfo.FromStream(paramSfo); pkg.ParamSfo = new SfoEntry(sfo); string date = "", time = ""; if ((project.volume.Package.CreationDate ?? "") == "") { date = "c_date=" + DateTime.UtcNow.ToString("yyyyMMdd"); } else { var split = project.volume.Package.CreationDate.Split(' '); var dateTime = DateTime.Parse(project.volume.Package.CreationDate).ToUniversalTime(); if (split.Length == 2) // Date and time specified { date = "c_date=" + dateTime.ToString("yyyyMMdd"); time = ",c_time=" + dateTime.ToString("HHmmss"); } else // just date is specified { date = "c_date=" + dateTime.ToString("yyyyMMdd"); } } sfo.Values.Add(new SFO.Utf8Value("PUBTOOLINFO", date + time, 0x200)); sfo.Values.Add(new SFO.IntegerValue("PUBTOOLVER", 0x02890000)); } pkg.PsReservedDat = new GenericEntry(EntryId.PSRESERVED_DAT) { FileData = new byte[0x2000] }; pkg.Entries = new List <Entry> { pkg.EntryKeys, pkg.ImageKey, pkg.GeneralDigests, pkg.Metas, pkg.Digests, pkg.EntryNames, pkg.LicenseDat, pkg.LicenseInfo, pkg.ParamSfo, pkg.PsReservedDat }; pkg.Digests.FileData = new byte[pkg.Entries.Count * Pkg.HASH_SIZE]; // 1st pass: set names foreach (var entry in pkg.Entries) { pkg.EntryNames.GetOffset(entry.Name); } // 2nd pass: set sizes, offsets in meta table var dataOffset = 0x2000u; var flagMap = new Dictionary <EntryId, uint>() { { EntryId.DIGESTS, 0x40000000 }, { EntryId.ENTRY_KEYS, 0x60000000 }, { EntryId.IMAGE_KEY, 0xE0000000 }, { EntryId.GENERAL_DIGESTS, 0x60000000 }, { EntryId.METAS, 0x60000000 }, { EntryId.ENTRY_NAMES, 0x40000000 }, { EntryId.LICENSE_DAT, 0x80000000 }, { EntryId.LICENSE_INFO, 0x80000000 }, }; var keyMap = new Dictionary <EntryId, uint> { { EntryId.IMAGE_KEY, 3u << 12 }, { EntryId.LICENSE_DAT, 3u << 12 }, { EntryId.LICENSE_INFO, 2u << 12 }, }; foreach (var entry in pkg.Entries) { var e = new MetaEntry { id = entry.Id, NameTableOffset = pkg.EntryNames.GetOffset(entry.Name), DataOffset = dataOffset, DataSize = entry.Length, // TODO Flags1 = flagMap.GetOrDefault(entry.Id), Flags2 = keyMap.GetOrDefault(entry.Id), }; pkg.Metas.Metas.Add(e); if (entry == pkg.Metas) { e.DataSize = (uint)pkg.Entries.Count * 32; } dataOffset += e.DataSize; var align = dataOffset % 16; if (align != 0) { dataOffset += 16 - align; } entry.meta = e; } pkg.Metas.Metas.Sort((e1, e2) => e1.id.CompareTo(e2.id)); pkg.Header.entry_count = (uint)pkg.Entries.Count; pkg.Header.entry_count_2 = (ushort)pkg.Entries.Count; pkg.Header.body_size = dataOffset; return(pkg); }
/// <summary> /// Decrypts the given bytes using the entry encryption. /// </summary> public static byte[] Decrypt(byte[] entryBytes, string contentId, string passcode, MetaEntry meta) { return(Decrypt(entryBytes, Crypto.ComputeKeys(contentId, passcode, meta.KeyIndex), meta)); }