Example #1
0
        /// <summary>
        /// Computes the final digests and writes them to the PKG
        /// </summary>
        /// <param name="pkgStream">PKG file stream</param>
        private void FinishPkg(Stream pkgStream)
        {
            Logger("Calculating SHA256 of finished outer PFS...");
            // Set PFS Image 1st block and full SHA256 hashes (mount image)
            pkg.Header.pfs_signed_digest = Crypto.Sha256(pkgStream, (long)pkg.Header.pfs_image_offset, 0x10000);
            pkg.Header.pfs_image_digest  = Crypto.Sha256(pkgStream, (long)pkg.Header.pfs_image_offset, (long)pkg.Header.pfs_image_size);

            foreach (var a in pkg.CalcGeneralDigests())
            {
                pkg.GeneralDigests.Set(a.Key, a.Value);
            }

            // Write body now because it will make calculating hashes easier.
            var writer = new PkgWriter(pkgStream);

            writer.WriteBody(pkg, project.ContentId, project.Passcode);

            CalcBodyDigests(pkg, pkgStream);

            // Now write header
            pkgStream.Position = 0;
            writer.WriteHeader(pkg.Header);

            // Final Pkg digest and signature
            pkg.HeaderDigest   = Crypto.Sha256(pkgStream, 0, 0xFE0);
            pkgStream.Position = 0xFE0;
            pkgStream.Write(pkg.HeaderDigest, 0, pkg.HeaderDigest.Length);
            byte[] header_sha256 = Crypto.Sha256(pkgStream, 0, 0x1000);
            pkgStream.Position = 0x1000;
            pkgStream.Write(pkg.HeaderSignature = Crypto.RSA2048EncryptKey(Keys.PkgSignKey, header_sha256), 0, 256);
        }
Example #2
0
        /// <summary>
        /// Writes your PKG to the given stream.
        /// Assumes exclusive use of the stream (writes are absolute, relative to 0)
        /// </summary>
        /// <param name="s"></param>
        /// <returns>Completed Pkg structure</returns>
        public Pkg Write(Stream s)
        {
            var pkg    = BuildPkg();
            var writer = new PkgWriter(s);

            // Write PFS first, to get stream length
            s.Position = (long)pkg.Header.pfs_image_offset;
            var pfsStream = new OffsetStream(s, s.Position);

            new PFS.PfsBuilder().BuildPfs(new PFS.PfsProperties
            {
                BlockSize = 65536,
                output    = pfsStream,
                proj      = project,
                projDir   = projectDir,
            });
            var align = s.Length % 65536;

            if (align != 0)
            {
                s.SetLength(s.Length + 65536 - align);
            }

            // TODO: Encrypt PFS (could also be done while writing image)
            // TODO: Generate hashes in Entries (body)
            // TODO: Calculate keys in entries (image key, etc)

            // Write body now because it will make calculating hashes easier.
            writer.WriteBody(pkg);

            CalcHeaderHashes(pkg, s);

            // Update header sizes now that we know how big things are...
            UpdateHeaderInfo(pkg, s.Length, pfsStream.Length);

            // Now write header
            s.Position = 0;
            writer.WritePkg(pkg);
            return(pkg);
        }
Example #3
0
        /// <summary>
        /// Writes your PKG to the given stream.
        /// Assumes exclusive use of the stream (writes are absolute, relative to 0)
        /// </summary>
        /// <param name="s"></param>
        /// <returns>Completed Pkg structure</returns>
        public Pkg Write(Stream s)
        {
            var pkg    = BuildPkg();
            var writer = new PkgWriter(s);

            // Write PFS first, to get stream length
            s.Position = (long)pkg.Header.pfs_image_offset;
            var EKPFS     = Crypto.ComputeKeys(project.volume.Package.ContentId, project.volume.Package.Passcode, 1);
            var pfsStream = new OffsetStream(s, s.Position);

            Console.WriteLine("Preparing inner PFS...");
            var innerPfs = new PFS.PfsBuilder(PFS.PfsProperties.MakeInnerPFSProps(project, projectDir), x => Console.WriteLine($"[innerpfs] {x}"));

            Console.WriteLine("Preparing outer PFS...");
            var outerPfs = new PFS.PfsBuilder(PFS.PfsProperties.MakeOuterPFSProps(project, innerPfs, EKPFS), x => Console.WriteLine($"[outerpfs] {x}"));

            outerPfs.WriteImage(pfsStream);

            // Update header sizes now that we know how big things are...
            UpdateHeaderInfo(pkg, s.Length, pfsStream.Length);
            pkg.Header.body_size = pkg.Header.pfs_image_offset - pkg.Header.body_offset;

            // Set PFS Image 1st block and full SHA256 hashes (mount image)
            pkg.Header.pfs_signed_digest = Crypto.Sha256(s, (long)pkg.Header.pfs_image_offset, 0x10000);
            pkg.Header.pfs_image_digest  = Crypto.Sha256(s, (long)pkg.Header.pfs_image_offset, (long)pkg.Header.pfs_image_size);

            if (pkg.ParamSfo.ParamSfo.GetValueByName("PUBTOOLINFO") is SFO.Utf8Value v)
            {
                v.Value +=
                    $",img0_l0_size={pfsStream.Length / (1000 * 1000)}" +
                    $",img0_l1_size=0" +
                    $",img0_sc_ksize=512" +
                    $",img0_pc_ksize=832";
            }
            // TODO: Generate hashes in Entries (body)
            var majorParamString =
                "ATTRIBUTE" + pkg.ParamSfo.ParamSfo.GetValueByName("ATTRIBUTE") +
                "CATEGORY" + pkg.ParamSfo.ParamSfo.GetValueByName("CATEGORY") +
                "FORMAT" + pkg.ParamSfo.ParamSfo.GetValueByName("FORMAT") +
                "PUBTOOLVER" + pkg.ParamSfo.ParamSfo.GetValueByName("PUBTOOLVER");

            pkg.GeneralDigests.Set(GeneralDigest.MajorParamDigest,
                                   Crypto.Sha256(Encoding.ASCII.GetBytes(majorParamString)));
            using (var ms = new MemoryStream())
            {
                ms.Write(Encoding.ASCII.GetBytes(pkg.Header.content_id), 0, 36);
                ms.Write(new byte[12], 0, 12);
                ms.WriteInt32BE((int)pkg.Header.drm_type);
                ms.WriteInt32BE((int)pkg.Header.content_type);

                if (pkg.Header.content_type == ContentType.AC ||
                    pkg.Header.content_type == ContentType.GD ||
                    pkg.Header.content_flags.HasFlag(ContentFlags.GD_AC))
                {
                    ms.Write(pkg.Header.pfs_image_digest, 0, 32);
                }
                ms.Write(pkg.GeneralDigests.Digests[GeneralDigest.MajorParamDigest], 0, 32);
                pkg.GeneralDigests.Set(GeneralDigest.ContentDigest, Crypto.Sha256(ms));
            }
            pkg.GeneralDigests.Set(GeneralDigest.GameDigest, pkg.Header.pfs_image_digest);
            using (var ms = new MemoryStream())
            {
                new PkgWriter(ms).WriteHeader(pkg.Header);
                using (var hash = System.Security.Cryptography.SHA256.Create())
                {
                    ms.Position = 0;
                    hash.TransformBlock(ms.ReadBytes(64), 0, 64, null, 0);
                    ms.Position = 0x400;
                    hash.TransformFinalBlock(ms.ReadBytes(128), 0, 128);
                    pkg.GeneralDigests.Set(GeneralDigest.HeaderDigest, hash.Hash);
                }
            }
            pkg.GeneralDigests.Set(GeneralDigest.ParamDigest, Crypto.Sha256(pkg.ParamSfo.ParamSfo.Serialize()));
            pkg.ImageKey.FileData = Crypto.RSA2048EncryptKey(RSAKeyset.FakeKeyset.Modulus, EKPFS);

            // Write body now because it will make calculating hashes easier.
            writer.WriteBody(pkg, project.volume.Package.ContentId, project.volume.Package.Passcode);

            CalcHeaderHashes(pkg, s);

            // Now write header
            s.Position = 0;
            writer.WritePkg(pkg);

            // Pkg Signature
            byte[] header_sha256 = Crypto.Sha256(s, 0, 0x1000);
            s.Position = 0x1000;
            s.Write(Crypto.RSA2048EncryptKey(Keys.PkgSignKey, header_sha256), 0, 256);

            return(pkg);
        }