internal byte[] GetEntryHash(int entryindex) { if (entryindex >= PFDEntries.entries.Count) { throw new Exception("entryindex is out of bounds"); } PFDEntry ent = PFDEntries.entries[entryindex]; ulong tableindex = CalculateHashTableEntryIndex(ent.file_name); ulong currententryindex = PFDHashTable.entries[(int)tableindex]; if (currententryindex < PFDHashTable.num_reserved) { var sha1 = new HMACSHA1(realkey); var hashdata = new List <byte>(); while (currententryindex < PFDHashTable.num_reserved) { ent = PFDEntries.entries[(int)currententryindex]; hashdata.AddRange(ent.HashData); currententryindex = ent.additional_index; } sha1.ComputeHash(hashdata.ToArray()); hashdata.Clear(); return(sha1.Hash); } return(null); }
/// <summary>Decrypts System.IO.Stream into a Byte Array</summary> /// <param name="stream"> /// the input stream of the file that is located inside the Param.PFD Entries /// </param> /// <param name="entryname">the name of the entry that the stream belongs to</param> /// <returns>byte array of the decrypted file</returns> public byte[] Decrypt(Stream stream, string entryname) { if (SecureFileID == null || SecureFileID.Length != 16) { DoProgress( (SecureFileID == null ? "SecureFileID needed to preform the encryption!" : "SecureFileID is not valid! length must be 16 bytes long (128bit)"), MessageType.Error); return(null); } var ent = new PFDEntry(); var found = false; foreach (var t in PFDEntries.entries) { if (t.file_name.ToLower() == entryname.ToLower()) { ent = t; found = true; break; } } if (!found) { throw new Exception("entryname does not exist inside the initialized Param.PFD"); } if (!stream.CanRead || !stream.CanWrite) { throw new Exception("Unable to Access stream"); } /* * This is currently disabled, since I got a lot of false-positives. * if (!ValidEntryHash(stream, entryname, false)) * throw new Exception( * "Encrypted data seems to be invalid, a validated file is required for this operation"); */ var size = AlignedSize((int)stream.Length); DoProgress("Allocating memory (" + size + " bytes)..", MessageType.Info); var data = new byte[size]; stream.Seek(0, SeekOrigin.Begin); stream.Read(data, 0, data.Length); DoProgress("Allocating decryption key..", MessageType.Info); var key = GetEntryKey(ent); DoProgress("Decrypting data (" + size + " bytes)..", MessageType.Info); data = Functions.Decrypt(key, data, size); if (data == null) { throw new Exception("Unable to decrypt data"); } DoProgress("Free memory..", MessageType.Info); key = null; DoProgress("Resizing data to its original size..", MessageType.Info); Array.Resize(ref data, (int)ent.file_size); return(data); }
public int EncryptAllFiles(string root) { try { int encrypted = 0; for (int i = 0; i < PFDEntries.entries.Count; i++) { PFDEntry t = PFDEntries.entries[i]; if (t.file_name.ToLower() == "param.sfo") { continue; } string filepath = root + "\\" + t.file_name; if (File.Exists(filepath)) { if (!ValidEntryHash(filepath, false)) { if (Encrypt(filepath)) { if (!ValidEntryHash(filepath, true)) { return(-1); } encrypted++; } } } } return(encrypted); } catch { return(-1); } }
/// <summary>Encrypt a specified System.IO.Stream to a byte array</summary> /// <param name="stream">Input System.IO.Stream of the file to encrypt</param> /// <param name="entryname">the name of the entry inside the PARAM.PFD</param> /// <returns>a byte array of theencrypted stream</returns> public byte[] Encrypt(Stream stream, string entryname) { if (SecureFileID == null || SecureFileID.Length != 16) { DoProgress( (SecureFileID == null ? "SecureFileID needed to preform the encryption!" : "SecureFileID is not valid! length must be 16 bytes long (128bit)"), MessageType.Error); return(null); } if (ValidEntryHash(stream, entryname, false)) { DoProgress("File already valid, same data will be returned instead", MessageType.Info); var data = new byte[stream.Length]; stream.Seek(0, SeekOrigin.Begin); stream.Read(data, 0, data.Length); return(data); } else { var ent = new PFDEntry(); var found = false; foreach (var t in PFDEntries.entries) { if (t.file_name.ToLower() == entryname.ToLower()) { ent = t; found = true; break; } } if (!found) { throw new Exception("entryname does not exist inside the initialized Param.PFD"); } if (!stream.CanRead || !stream.CanWrite) { throw new Exception("Unable to Access stream"); } ent.file_size = (ulong)stream.Length; stream.Seek(0, SeekOrigin.Begin); var size = AlignedSize((int)stream.Length); DoProgress("Allocating memory (" + size + " bytes)..", MessageType.Info); var data = new byte[size]; DoProgress("Reading stream into memory..", MessageType.Info); stream.Read(data, 0, (int)stream.Length); DoProgress("Allocating encryption key..", MessageType.Info); var key = GetEntryKey(ent); DoProgress("Encrypting Data (" + size + "bytes)..", MessageType.Info); data = Functions.Encypt(key, data, data.Length); if (data == null) { throw new Exception("Unable to decrypt data"); } DoProgress("Free allocated memory..", MessageType.Info); key = null; return(data); } }
/// <summary> /// Encrypts specified filepath /// </summary> /// <param name="filepath">the filepath to encrypt, file must be in the same directory as the PARAM.PFD</param> /// <returns>true if file is succesfully encrypted</returns> public bool Encrypt(string filepath) { try { if (!File.Exists(filepath)) { DoProgress(filepath + " Does not exist!", MessageType.Error); return(false); } string name = new FileInfo(filepath).Name; if (!EntryExists(name)) { DoProgress("There is no \"" + name + "\" inside the PARAM.PFD Entries!", MessageType.Error); return(false); } DoProgress("Initializing file stream..", MessageType.Info); byte[] data = null; using (FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { for (int i = 0; i < PFDEntries.entries.Count; i++) { if (PFDEntries.entries[i].file_name.ToLower() == name.ToLower()) { PFDEntry t = PFDEntries.entries[i]; t.file_size = (ulong)fs.Length; PFDEntries.entries[i] = t; break; } } data = Encrypt(fs, name); //DoProgress("Rehashing PARAM.PFD..", MessageType.Info); //bool x = ValidEntryHash(fs, name, true) && ValidFileCID(true) && ValidDHKCID2(true) && ValidBottomHash(true); fs.Dispose(); } if (data == null) { return(false); } DoProgress("Writing Encrypted data to : " + filepath, MessageType.Info); File.WriteAllBytes(filepath, data); DoProgress(name + " is succesfully encrypted", MessageType.Info); return(true); } catch (Exception ex) { DoProgress(ex.Message, MessageType.Error); return(false); } }
internal byte[] GetEntryHashKey(PFDEntry entry, int hashindex) { switch (entry.file_name.ToLower()) { case "param.sfo": return(GenerateHashkeyForSFO(hashindex)); case "tropsys.dat": return(Functions.GetStaticKey("tropsys_dat_key")); case "tropusr.dat": return(Functions.GetStaticKey("tropusr_dat_key")); case "troptrns.dat": return(Functions.GetStaticKey("troptrns_dat_key")); case "tropconf.sfm": return(Functions.GetStaticKey("tropconf_sfm_key")); default: return(GerateHashKeyForSecureFileID(SecureFileID)); } }
internal void Init(Stream input) { DoProgress("Initializing Param.PFD stream..", MessageType.Info); using (var br = new BinaryReader(input)) { PFDHeader.magic = br.ReadUInt64().SwapByteOrder(); if (PFDHeader.magic != 0x50464442ul) { DoProgress("Invalid PFD File!", MessageType.Error); throw new Exception("Invalid PFD File!"); } PFDHeader.version = br.ReadUInt64().SwapByteOrder(); if (PFDHeader.version != 3ul && PFDHeader.version != 4ul) { DoProgress("Unsupported PFD version!", MessageType.Error); throw new Exception("Unsupported PFD version!"); } DoProgress("Allocating Header Data..", MessageType.Info); pfdheaderkey = br.ReadBytes(16); var header = br.ReadBytes(64); header = Functions.DecryptWithPortability(pfdheaderkey, header, header.Length); PFDSignature.bottom_hash = new byte[20]; Array.Copy(header, 0, PFDSignature.bottom_hash, 0, 20); PFDSignature.top_hash = new byte[20]; Array.Copy(header, 20, PFDSignature.top_hash, 0, 20); PFDSignature.hash_key = new byte[20]; Array.Copy(header, 40, PFDSignature.hash_key, 0, 20); PFDSignature.padding = new byte[4]; Array.Copy(header, 60, PFDSignature.padding, 0, 4); header = null; if (PFDHeader.version == 4ul) { realkey = Functions.GetHMACSHA1(Functions.GetStaticKey("keygen_key"), PFDSignature.hash_key, 0, 20); } else { realkey = PFDSignature.hash_key; } DoProgress("Reading Entries..", MessageType.Info); PFDHashTable.capacity = br.ReadUInt64().SwapByteOrder(); PFDHashTable.num_reserved = br.ReadUInt64().SwapByteOrder(); PFDHashTable.num_used = br.ReadUInt64().SwapByteOrder(); PFDHashTable.entries = new List <ulong>(); DoProgress("Reading table capicity (" + PFDHashTable.capacity + " entries)..", MessageType.Info); for (ulong i = 0; i < PFDHashTable.capacity; i++) { PFDHashTable.entries.Add(br.ReadUInt64().SwapByteOrder()); } PFDEntries.entries = new List <PFDEntry>(); DoProgress("Reading used tables (" + PFDHashTable.num_used + " entries)..", MessageType.Info); for (ulong i = 0; i < PFDHashTable.num_used; i++) { var x = new PFDEntry { additional_index = br.ReadUInt64().SwapByteOrder(), file_name = Encoding.ASCII.GetString(br.ReadBytes(65)).Replace("\0", ""), __padding_0 = br.ReadBytes(7), key = br.ReadBytes(64), file_hashes = new List <byte[]>() }; for (var j = 0; j < 4; j++) { x.file_hashes.Add(br.ReadBytes(20)); } x.__padding_1 = br.ReadBytes(40); x.file_size = br.ReadUInt64().SwapByteOrder(); PFDEntries.entries.Add(x); } var offset = (long) ((ulong)(br.BaseStream.Position) + (0x110 * (PFDHashTable.num_reserved - PFDHashTable.num_used))); br.BaseStream.Position = offset; PFDEntrySignatureTable.hashes = new List <byte[]>(); DoProgress("Reading file table hashes (" + PFDHashTable.capacity + " entries)..", MessageType.Info); for (ulong i = 0; i < PFDHashTable.capacity; i++) { PFDEntrySignatureTable.hashes.Add(br.ReadBytes(20)); } br.Close(); } }
internal byte[] GetEntryKey(PFDEntry entry) { var key = GetEntryHashKey(entry, 0); return(Functions.DecryptWithPortability(key, entry.key, entry.key.Length)); }
internal void Init(Stream input) { DoProgress("Initializing Param.PFD stream..", MessageType.Info); using (var br = new BinaryReader(input)) { PFDHeader.magic = br.ReadUInt64().SwapByteOrder(); if (PFDHeader.magic != 0x50464442ul) { DoProgress("Invalid PFD File!", MessageType.Error); throw new Exception("Invalid PFD File!"); } PFDHeader.version = br.ReadUInt64().SwapByteOrder(); if (PFDHeader.version != 3ul && PFDHeader.version != 4ul) { DoProgress("Unsupported PFD version!", MessageType.Error); throw new Exception("Unsupported PFD version!"); } DoProgress("Allocating Header Data..", MessageType.Info); pfdheaderkey = br.ReadBytes(16); byte[] header = br.ReadBytes(64); header = Functions.DecryptWithPortability(pfdheaderkey, header, header.Length); PFDSignature.bottom_hash = new byte[20]; Array.Copy(header, 0, PFDSignature.bottom_hash, 0, 20); PFDSignature.top_hash = new byte[20]; Array.Copy(header, 20, PFDSignature.top_hash, 0, 20); PFDSignature.hash_key = new byte[20]; Array.Copy(header, 40, PFDSignature.hash_key, 0, 20); PFDSignature.padding = new byte[4]; Array.Copy(header, 60, PFDSignature.padding, 0, 4); header = null; if (PFDHeader.version == 4ul) realkey = Functions.GetHMACSHA1(Functions.GetStaticKey("keygen_key"), PFDSignature.hash_key, 0, 20); else realkey = PFDSignature.hash_key; DoProgress("Reading Entries..", MessageType.Info); PFDHashTable.capacity = br.ReadUInt64().SwapByteOrder(); PFDHashTable.num_reserved = br.ReadUInt64().SwapByteOrder(); PFDHashTable.num_used = br.ReadUInt64().SwapByteOrder(); PFDHashTable.entries = new List<ulong>(); DoProgress("Reading table capicity (" + PFDHashTable.capacity + " entries)..", MessageType.Info); for (ulong i = 0; i < PFDHashTable.capacity; i++) PFDHashTable.entries.Add(br.ReadUInt64().SwapByteOrder()); PFDEntries.entries = new List<PFDEntry>(); DoProgress("Reading used tables (" + PFDHashTable.num_used + " entries)..", MessageType.Info); for (ulong i = 0; i < PFDHashTable.num_used; i++) { var x = new PFDEntry(); x.additional_index = br.ReadUInt64().SwapByteOrder(); x.file_name = Encoding.ASCII.GetString(br.ReadBytes(65)).Replace("\0", ""); x.__padding_0 = br.ReadBytes(7); x.key = br.ReadBytes(64); x.file_hashes = new List<byte[]>(); for (int j = 0; j < 4; j++) x.file_hashes.Add(br.ReadBytes(20)); x.__padding_1 = br.ReadBytes(40); x.file_size = br.ReadUInt64().SwapByteOrder(); PFDEntries.entries.Add(x); } var offset = (long) ((ulong) (br.BaseStream.Position) + (0x110*(PFDHashTable.num_reserved - PFDHashTable.num_used))); br.BaseStream.Position = offset; PFDEntrySignatureTable.hashes = new List<byte[]>(); DoProgress("Reading file table hashes (" + PFDHashTable.capacity + " entries)..", MessageType.Info); for (ulong i = 0; i < PFDHashTable.capacity; i++) PFDEntrySignatureTable.hashes.Add(br.ReadBytes(20)); br.Close(); } }
internal byte[] GetEntryKey(PFDEntry entry) { byte[] key = GetEntryHashKey(entry, 0); return Functions.DecryptWithPortability(key, entry.key, entry.key.Length); }
internal byte[] GetEntryHashKey(PFDEntry entry, int hashindex) { switch (entry.file_name.ToLower()) { case "param.sfo": return GenerateHashkeyForSFO(hashindex); case "tropsys.dat": return Functions.GetStaticKey("tropsys_dat_key"); case "tropusr.dat": return Functions.GetStaticKey("tropusr_dat_key"); case "troptrns.dat": return Functions.GetStaticKey("troptrns_dat_key"); case "tropconf.sfm": return Functions.GetStaticKey("tropconf_sfm_key"); default: return GerateHashKeyForSecureFileID(SecureFileID); } }
/// <summary> /// Encrypt a specified System.IO.Stream to a byte array /// </summary> /// <param name="stream">Input System.IO.Stream of the file to encrypt</param> /// <param name="entryname">the name of the entry inside the PARAM.PFD</param> /// <returns>a byte array of theencrypted stream</returns> public byte[] Encrypt(Stream stream, string entryname) { if (SecureFileID == null || SecureFileID.Length != 16) { DoProgress( (SecureFileID == null ? "SecureFileID needed to preform the encryption!" : "SecureFileID is not valid! length must be 16 bytes long (128bit)"), MessageType.Error); return null; } if (ValidEntryHash(stream, entryname, false)) { DoProgress("File already valid, same data will be returned instead", MessageType.Info); var data = new byte[stream.Length]; stream.Seek(0, SeekOrigin.Begin); stream.Read(data, 0, data.Length); return data; } else { var ent = new PFDEntry(); bool found = false; foreach (PFDEntry t in PFDEntries.entries) { if (t.file_name.ToLower() == entryname.ToLower()) { ent = t; found = true; break; } } if (!found) throw new Exception("entryname does not exist inside the initialized Param.PFD"); if (!stream.CanRead || !stream.CanWrite) throw new Exception("Unable to Access stream"); ent.file_size = (ulong) stream.Length; stream.Seek(0, SeekOrigin.Begin); int size = AlignedSize((int) stream.Length); DoProgress("Allocating memory (" + size + " bytes)..", MessageType.Info); var data = new byte[size]; DoProgress("Reading stream into memory..", MessageType.Info); stream.Read(data, 0, (int) stream.Length); DoProgress("Allocating encryption key..", MessageType.Info); byte[] key = GetEntryKey(ent); DoProgress("Encrypting Data (" + size + "bytes)..", MessageType.Info); data = Functions.Encypt(key, data, data.Length); if (data == null) throw new Exception("Unable to decrypt data"); DoProgress("Free allocated memory..", MessageType.Info); key = null; return data; } }
/// <summary> /// Decrypts System.IO.Stream into a Byte Array /// </summary> /// <param name="stream">the input stream of the file that is located inside the Param.PFD Entries</param> /// <param name="entryname">the name of the entry that the stream belongs to</param> /// <returns>byte array of the decrypted file</returns> public byte[] Decrypt(Stream stream, string entryname) { if (SecureFileID == null || SecureFileID.Length != 16) { DoProgress( (SecureFileID == null ? "SecureFileID needed to preform the encryption!" : "SecureFileID is not valid! length must be 16 bytes long (128bit)"), MessageType.Error); return null; } var ent = new PFDEntry(); bool found = false; foreach (PFDEntry t in PFDEntries.entries) if (t.file_name.ToLower() == entryname.ToLower()) { ent = t; found = true; break; } if (!found) throw new Exception("entryname does not exist inside the initialized Param.PFD"); if (!stream.CanRead || !stream.CanWrite) throw new Exception("Unable to Access stream"); if (!ValidEntryHash(stream, entryname, false)) throw new Exception( "Encrypted data seems to be invalid, a validated file is required for this operation"); int size = AlignedSize((int) stream.Length); DoProgress("Allocating memory (" + size + " bytes)..", MessageType.Info); var data = new byte[size]; stream.Seek(0, SeekOrigin.Begin); stream.Read(data, 0, data.Length); DoProgress("Allocating decryption key..", MessageType.Info); byte[] key = GetEntryKey(ent); DoProgress("Decrypting data (" + size + " bytes)..", MessageType.Info); data = Functions.Decrypt(key, data, size); if (data == null) throw new Exception("Unable to decrypt data"); DoProgress("Free memory..", MessageType.Info); key = null; DoProgress("Resizing data to its original size..", MessageType.Info); Array.Resize(ref data, (int) ent.file_size); return data; }