Exemple #1
0
 /// <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));
 }
Exemple #2
0
        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);
        }
Exemple #4
0
        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);
        }
Exemple #5
0
        /// <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);
        }
Exemple #6
0
        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
            });
        }
Exemple #7
0
        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,
            });
        }
Exemple #8
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        /// <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);
            }
        }
Exemple #12
0
        /// <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);
        }
Exemple #13
0
        /// <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);
        }
Exemple #14
0
 /// <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));
 }