Initial commit
This commit is contained in:
parent
04bafb8b70
commit
7b6830a40b
|
@ -0,0 +1,21 @@
|
|||
*/bin/*
|
||||
*/obj/*
|
||||
.vs/*
|
||||
*.7z
|
||||
|
||||
|
||||
PbpResign/bin/*
|
||||
PbpResign/obj/*
|
||||
|
||||
PopsBuilder/bin/*
|
||||
PopsBuilder/obj/*
|
||||
|
||||
|
||||
PspCrypto/bin/*
|
||||
PspCrypto/obj/*
|
||||
|
||||
PsvImage/bin/*
|
||||
PsvImage/obj/*
|
||||
|
||||
UnicornTest/*
|
||||
UnicornManaged/*
|
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PopsBuilder\PopsBuilder.csproj" />
|
||||
<ProjectReference Include="..\PspCrypto\PspCrypto.csproj" />
|
||||
<ProjectReference Include="..\PsvImage\PsvImage.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,78 @@
|
|||
using CommunityToolkit.HighPerformance;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PbpResign
|
||||
{
|
||||
internal class Sfo
|
||||
{
|
||||
private struct SfoHeader
|
||||
{
|
||||
public uint Magic;
|
||||
public uint Version;
|
||||
public int KeyTableStart;
|
||||
public int DataTableStart;
|
||||
public int TablesEntries;
|
||||
}
|
||||
|
||||
private struct SfoIndexTableEntry
|
||||
{
|
||||
public ushort KeyOffset;
|
||||
public ushort DataFormat;
|
||||
public int DataLen;
|
||||
public int DataMaxLen;
|
||||
public int DataOffset;
|
||||
}
|
||||
|
||||
private const ushort PSF_TYPE_BIN = 0x0004;
|
||||
private const ushort PSF_TYPE_STR = 0x0204;
|
||||
private const ushort PSF_TYPE_VAL = 0x0404;
|
||||
|
||||
public static Dictionary<string, object> ReadSfo(ReadOnlySpan<byte> sfo)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
var hdr = MemoryMarshal.Read<SfoHeader>(sfo);
|
||||
if (hdr.Magic == 0x46535000)
|
||||
{
|
||||
dic = new Dictionary<string, object>(hdr.TablesEntries);
|
||||
var entries = MemoryMarshal.Cast<byte, SfoIndexTableEntry>(sfo.Slice(20, hdr.TablesEntries * 16));
|
||||
unsafe
|
||||
{
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var keyOffset = hdr.KeyTableStart + entry.KeyOffset;
|
||||
string keyName;
|
||||
fixed (byte* ptr = sfo[keyOffset..])
|
||||
{
|
||||
var strData = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(ptr);
|
||||
keyName = Encoding.UTF8.GetString(strData);
|
||||
}
|
||||
|
||||
var dataOffset = hdr.DataTableStart + entry.DataOffset;
|
||||
var dataLen = entry.DataLen;
|
||||
var maxLen = entry.DataMaxLen;
|
||||
var data = sfo.Slice(dataOffset, dataLen);
|
||||
|
||||
switch (entry.DataFormat)
|
||||
{
|
||||
case PSF_TYPE_BIN:
|
||||
dic[keyName] = data.ToArray();
|
||||
break;
|
||||
case PSF_TYPE_STR:
|
||||
dic[keyName] = Encoding.UTF8.GetString(data).TrimEnd('\0');
|
||||
break;
|
||||
case PSF_TYPE_VAL:
|
||||
dic[keyName] = MemoryMarshal.Read<uint>(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dic;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
using Org.BouncyCastle.Crypto.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace PopsBuilder.Atrac3
|
||||
{
|
||||
public class Atrac3ToolEncoder : IAtracEncoderBase
|
||||
{
|
||||
private static Random rng = new Random();
|
||||
private static string TOOLS_DIRECTORY = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tools");
|
||||
|
||||
private static string AT3TOOL_WIN = Path.Combine(TOOLS_DIRECTORY, "at3tool.exe");
|
||||
private static string AT3TOOL_LINUX = Path.Combine(TOOLS_DIRECTORY, "at3tool.elf");
|
||||
|
||||
private static string TEMP_DIRECTORY = Path.Combine(Path.GetTempPath(), "at3tool_tmp");
|
||||
|
||||
// random name so that can generate multiple at once if wanted ..
|
||||
private string TEMP_WAV;
|
||||
private string TEMP_AT3;
|
||||
public Atrac3ToolEncoder()
|
||||
{
|
||||
string rdmPart = rng.Next().ToString("X");
|
||||
|
||||
TEMP_WAV = Path.Combine(TEMP_DIRECTORY, rdmPart + "_tmp.wav");
|
||||
TEMP_AT3 = Path.Combine(TEMP_DIRECTORY, rdmPart + "_tmp.at3");
|
||||
|
||||
}
|
||||
|
||||
private static string AT3TOOL_LOCATION
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
return AT3TOOL_WIN;
|
||||
else if (OperatingSystem.IsLinux())
|
||||
return AT3TOOL_LINUX;
|
||||
else
|
||||
throw new PlatformNotSupportedException("No at3tool binary for your platform");
|
||||
}
|
||||
}
|
||||
|
||||
private void runAtrac3Tool()
|
||||
{
|
||||
using(Process proc = new Process())
|
||||
{
|
||||
proc.StartInfo.FileName = AT3TOOL_LOCATION;
|
||||
proc.StartInfo.Arguments = "-br 132 -e \"" + TEMP_WAV + "\" \"" + TEMP_AT3 + "\"";
|
||||
|
||||
proc.StartInfo.UseShellExecute = false;
|
||||
proc.StartInfo.CreateNoWindow = true;
|
||||
proc.StartInfo.RedirectStandardOutput = true;
|
||||
proc.StartInfo.RedirectStandardInput = true;
|
||||
|
||||
proc.Start();
|
||||
proc.WaitForExit();
|
||||
|
||||
string stdout = proc.StandardOutput.ReadToEnd();
|
||||
if (!stdout.Contains("Total Encoded Bytes"))
|
||||
throw new Exception(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] stripAtracHeader()
|
||||
{
|
||||
using(FileStream at3Stream = File.OpenRead(TEMP_AT3))
|
||||
{
|
||||
StreamUtil at3Util = new StreamUtil(at3Stream);
|
||||
at3Stream.Seek(0x4C, SeekOrigin.Begin);
|
||||
int at3Len = at3Util.ReadInt32();
|
||||
return at3Util.ReadBytes(at3Len);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeWav(byte[] pcmData)
|
||||
{
|
||||
using (FileStream wavStream = File.Open(TEMP_WAV, FileMode.Create))
|
||||
{
|
||||
// CD-AUDIO standard settings
|
||||
int fileSize = pcmData.Length;
|
||||
int samplerate = 44100;
|
||||
short channels = 2; // channels
|
||||
short format = 16; // signed, 16 bit PCM
|
||||
|
||||
StreamUtil wavUtil = new StreamUtil(wavStream);
|
||||
|
||||
wavUtil.WriteStr("RIFF");
|
||||
wavUtil.WriteInt32((fileSize + (0x2C - 8)));
|
||||
|
||||
wavUtil.WriteStr("WAVE");
|
||||
wavUtil.WriteStr("fmt ");
|
||||
wavUtil.WriteInt32(format);
|
||||
|
||||
wavUtil.WriteInt16(1);
|
||||
wavUtil.WriteInt16(channels);
|
||||
wavUtil.WriteInt32(samplerate);
|
||||
wavUtil.WriteInt32((samplerate * format * channels) / 8);
|
||||
wavUtil.WriteInt16(Convert.ToInt16(format * channels));
|
||||
wavUtil.WriteInt16(format);
|
||||
|
||||
wavUtil.WriteStr("data");
|
||||
wavUtil.WriteInt32(fileSize);
|
||||
wavUtil.WriteBytes(pcmData);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureFilesAvailable()
|
||||
{
|
||||
if (!Directory.Exists(TEMP_DIRECTORY))
|
||||
Directory.CreateDirectory(TEMP_DIRECTORY);
|
||||
|
||||
if (!Directory.Exists(TOOLS_DIRECTORY))
|
||||
Directory.CreateDirectory(TOOLS_DIRECTORY);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
if (!File.Exists(AT3TOOL_WIN))
|
||||
{
|
||||
throw new FileNotFoundException("Cannot find at3tool at " + AT3TOOL_WIN);
|
||||
}
|
||||
}
|
||||
else if(OperatingSystem.IsLinux())
|
||||
{
|
||||
if (!File.Exists(AT3TOOL_LINUX))
|
||||
{
|
||||
throw new FileNotFoundException("Cannot find at3tool at " + AT3TOOL_LINUX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup()
|
||||
{
|
||||
if (File.Exists(TEMP_WAV)) File.Delete(TEMP_WAV);
|
||||
if (File.Exists(TEMP_AT3)) File.Delete(TEMP_AT3);
|
||||
}
|
||||
|
||||
public byte[] EncodeToAtrac(byte[] pcmData)
|
||||
{
|
||||
ensureFilesAvailable();
|
||||
|
||||
makeWav(pcmData);
|
||||
runAtrac3Tool();
|
||||
byte[] rawAtracData = stripAtracHeader();
|
||||
|
||||
cleanup();
|
||||
|
||||
return rawAtracData;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Atrac3
|
||||
{
|
||||
public interface IAtracEncoderBase
|
||||
{
|
||||
public byte[] EncodeToAtrac(byte[] pcmData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Cue
|
||||
{
|
||||
public class CueIndex
|
||||
{
|
||||
public byte IndexNumber;
|
||||
public short Mrel;
|
||||
public short Srel;
|
||||
public short Frel;
|
||||
|
||||
public short Mdelta;
|
||||
public short Sdelta;
|
||||
public short Fdelta;
|
||||
|
||||
internal int mTtl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Mrel + Mdelta);
|
||||
}
|
||||
}
|
||||
|
||||
internal int sTtl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Srel + Sdelta);
|
||||
}
|
||||
}
|
||||
|
||||
internal int fTtl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Frel + Fdelta);
|
||||
}
|
||||
}
|
||||
|
||||
public byte m
|
||||
{
|
||||
get
|
||||
{
|
||||
int carryF = Convert.ToInt32(Math.Floor(Convert.ToDouble(fTtl) / 75.0));
|
||||
int carryS = Convert.ToInt32(Math.Floor(Convert.ToDouble(sTtl + carryF) / 60.0));
|
||||
|
||||
return Convert.ToByte(mTtl + carryS);
|
||||
}
|
||||
}
|
||||
|
||||
public byte s
|
||||
{
|
||||
get
|
||||
{
|
||||
int carryF = Convert.ToInt32(Math.Floor(Convert.ToDouble(fTtl) / 75.0));
|
||||
|
||||
return Convert.ToByte(((Srel + Sdelta) + carryF) % 60);
|
||||
}
|
||||
}
|
||||
|
||||
public byte f
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToByte((fTtl) % 75);
|
||||
}
|
||||
}
|
||||
public byte M
|
||||
{
|
||||
get
|
||||
{
|
||||
return CueReader.BinaryDecimalConv(m);
|
||||
}
|
||||
}
|
||||
|
||||
public byte S
|
||||
{
|
||||
get
|
||||
{
|
||||
return CueReader.BinaryDecimalConv(s);
|
||||
}
|
||||
}
|
||||
|
||||
public byte F
|
||||
{
|
||||
get
|
||||
{
|
||||
return CueReader.BinaryDecimalConv(f);
|
||||
}
|
||||
}
|
||||
|
||||
internal CueIndex(byte indexNumber)
|
||||
{
|
||||
IndexNumber = indexNumber;
|
||||
Mrel = 0;
|
||||
Srel = 0;
|
||||
Frel = 0;
|
||||
Mdelta = 0;
|
||||
Sdelta = 0;
|
||||
Fdelta = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Cue
|
||||
{
|
||||
public class CueReader : IDisposable
|
||||
{
|
||||
public int FirstDataTrackNo
|
||||
{
|
||||
get
|
||||
{
|
||||
return getFirstDataTrackNo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private CueTrack[] tracks = new CueTrack[99];
|
||||
private Dictionary<int, CueStream> openTracks;
|
||||
|
||||
public int GetTotalTracks()
|
||||
{
|
||||
int totalTracks = 0;
|
||||
for(int i = 0; i < tracks.Length; i++)
|
||||
if (tracks[i] is not null) totalTracks++;
|
||||
return totalTracks;
|
||||
}
|
||||
|
||||
public static byte BinaryDecimalConv(int i)
|
||||
{
|
||||
return Convert.ToByte(Convert.ToInt32((i % 10) + 16 * ((i / 10) % 10)));
|
||||
}
|
||||
public int IdxToSectorRel(CueIndex index)
|
||||
{
|
||||
int offset = (((index.Mrel * 60) + index.Srel) * 75 + index.Frel);
|
||||
return offset;
|
||||
}
|
||||
public int IdxToSector(CueIndex index)
|
||||
{
|
||||
int offset = (((index.m * 60) + index.s) * 75 + index.f);
|
||||
return offset;
|
||||
}
|
||||
|
||||
public CueIndex SectorToIdx(int sector)
|
||||
{
|
||||
CueIndex idx = new CueIndex(1);
|
||||
|
||||
int x = sector;
|
||||
int f = sector % 75;
|
||||
x = x - f;
|
||||
x = Convert.ToInt32(Math.Floor(Convert.ToDouble(x) / 75.0));
|
||||
int s = x % 60;
|
||||
int m = Convert.ToInt32(Math.Floor(Convert.ToDouble(x) / 60.0));
|
||||
|
||||
//idx.Mrel = Convert.ToByte(Convert.ToInt32(((sector / 75) / 60)));
|
||||
//idx.Srel = Convert.ToByte(Convert.ToInt32(((sector / 75) % 60)));
|
||||
//idx.Frel = Convert.ToByte(Convert.ToInt32(((sector % 75))));
|
||||
//idx.Sdelta = 2; // why?
|
||||
|
||||
idx.Mrel = Convert.ToInt16(m);
|
||||
idx.Srel = Convert.ToInt16(s);
|
||||
idx.Frel = Convert.ToInt16(f);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
private int getFirstDataTrackNo()
|
||||
{
|
||||
foreach (CueTrack track in tracks)
|
||||
if (track is not null)
|
||||
if (track.TrackType == TrackType.TRACK_MODE2_2352) return track.TrackNo;
|
||||
|
||||
// no non-data tracks?
|
||||
return 1;
|
||||
}
|
||||
private void setTrackNumber(int trackNo, ref CueTrack? track)
|
||||
{
|
||||
tracks[trackNo - 1] = track;
|
||||
}
|
||||
public CueTrack GetTrackNumber(int trackNo)
|
||||
{
|
||||
return tracks[trackNo - 1];
|
||||
}
|
||||
private int findTrackSz(int trackNo)
|
||||
{
|
||||
CueTrack track = GetTrackNumber(trackNo);
|
||||
// total iso (size / sector size)
|
||||
int startSector = IdxToSector(track.TrackIndex[1]);
|
||||
int fileSectorSz = Convert.ToInt32(track.binFileSz / track.SectorSz);
|
||||
int endSector = Convert.ToInt32(startSector + fileSectorSz);
|
||||
|
||||
// check if another track begins (thus ending this one)
|
||||
for (int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
CueTrack? cTrack = tracks[i];
|
||||
if (cTrack is not null)
|
||||
{
|
||||
if (cTrack.TrackNo <= track.TrackNo) continue;
|
||||
int sector = IdxToSector(cTrack.TrackIndex[0]);
|
||||
|
||||
if (sector < endSector) endSector = sector;
|
||||
}
|
||||
}
|
||||
|
||||
int sectorsLength = (endSector - startSector);
|
||||
return sectorsLength;
|
||||
}
|
||||
|
||||
public CueStream OpenTrack(int trackNo)
|
||||
{
|
||||
if (!openTracks.ContainsKey(trackNo))
|
||||
{
|
||||
CueTrack track = GetTrackNumber(trackNo);
|
||||
int sectorStart = IdxToSectorRel(track.TrackIndex[1]);
|
||||
int sectorLen = findTrackSz(trackNo);
|
||||
|
||||
CueStream trackBin = new CueStream(File.OpenRead(track.binFileName), sectorStart * track.SectorSz, sectorLen * track.SectorSz);
|
||||
openTracks[trackNo] = trackBin;
|
||||
return trackBin;
|
||||
}
|
||||
else
|
||||
{
|
||||
CueStream openTrack = openTracks[trackNo];
|
||||
if (!openTrack.IsClosed)
|
||||
{
|
||||
openTracks.Remove(trackNo);
|
||||
return OpenTrack(trackNo);
|
||||
}
|
||||
else
|
||||
{
|
||||
openTracks[trackNo].Seek(0x00, SeekOrigin.Begin);
|
||||
return openTracks[trackNo];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int getLastTrackNo()
|
||||
{
|
||||
int trackNo = 0;
|
||||
for (int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
if (tracks[i] is null) continue;
|
||||
trackNo = tracks[i].TrackNo;
|
||||
}
|
||||
return trackNo;
|
||||
}
|
||||
private int getTotalSectorSz()
|
||||
{
|
||||
int sectors = 0;
|
||||
HashSet<string> countedBins = new HashSet<string>();
|
||||
|
||||
for(int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
if (tracks[i] is null) continue;
|
||||
if (!countedBins.Contains(tracks[i].binFileName))
|
||||
{
|
||||
countedBins.Add(tracks[i].binFileName);
|
||||
sectors += Convert.ToInt32(tracks[i].binFileSz / CueTrack.MODE2_SECTOR_SZ);
|
||||
}
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
private bool haveAudioTracks
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
if (tracks[i] is null) continue;
|
||||
if (tracks[i].TrackType == TrackType.TRACK_CDDA) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] createDummyTracks()
|
||||
{
|
||||
// every psn ps1 game have "A0" track that points to sector 6000 (MSF 01 20 00)
|
||||
byte[] tocA0Entry = new byte[10] { 0x41, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00 };
|
||||
|
||||
// And an A1 track (determines how many tracks there are)
|
||||
byte[] tocA1Entry = new byte[10] { 0x41, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, BinaryDecimalConv(GetTotalTracks()), 0x00, 0x00 };
|
||||
|
||||
// the A2 track is a bit more complicated ..
|
||||
int totalSectors = getTotalSectorSz();
|
||||
CueIndex idx = SectorToIdx(totalSectors);
|
||||
idx.Sdelta = 2;
|
||||
|
||||
byte[] tocA2Entry = new byte[10] { 0x41, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x00, idx.M, idx.S, idx.F };
|
||||
|
||||
if (GetTrackNumber(getLastTrackNo()).TrackType == TrackType.TRACK_CDDA)
|
||||
{
|
||||
tocA2Entry[0x00] = 0x01;
|
||||
tocA1Entry[0x00] = 0x01;
|
||||
}
|
||||
|
||||
byte[] tocDummy = new byte[10 * 3];
|
||||
Array.ConstrainedCopy(tocA0Entry, 0, tocDummy, 0, 10);
|
||||
Array.ConstrainedCopy(tocA1Entry, 0, tocDummy, 10, 10);
|
||||
Array.ConstrainedCopy(tocA2Entry, 0, tocDummy, 20, 10);
|
||||
|
||||
return tocDummy;
|
||||
}
|
||||
|
||||
private int getTrackSectorOnDisc(int trackNo)
|
||||
{
|
||||
int absolutePosition = 0;
|
||||
for(int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
if (tracks[i] is null) continue;
|
||||
if (tracks[i].TrackNo == trackNo) break;
|
||||
|
||||
absolutePosition += findTrackSz(tracks[i].TrackNo);
|
||||
}
|
||||
return absolutePosition;
|
||||
}
|
||||
|
||||
private void fixUpMsf()
|
||||
{
|
||||
|
||||
Dictionary<string, int> positions = new Dictionary<string, int>();
|
||||
int totalPosition = 0;
|
||||
|
||||
for (int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
if (tracks[i] is null) continue;
|
||||
|
||||
if (!positions.ContainsKey(tracks[i].binFileName))
|
||||
{
|
||||
positions[tracks[i].binFileName] = totalPosition;
|
||||
double sz = Convert.ToDouble(tracks[i].binFileSz) / Convert.ToDouble(tracks[i].SectorSz);
|
||||
|
||||
totalPosition += Convert.ToInt32(sz);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
if (tracks[i] is null) continue;
|
||||
int pos = positions[tracks[i].binFileName];
|
||||
|
||||
CueIndex idx = this.SectorToIdx(pos);
|
||||
// pregap not included on first track
|
||||
|
||||
if (tracks[i].TrackNo == 1) tracks[i].TrackIndex[0].Sdelta = 0;
|
||||
|
||||
// add pregap
|
||||
idx.Mdelta = Convert.ToInt16(tracks[i].TrackIndex[1].Mrel - tracks[i].TrackIndex[0].Mrel);
|
||||
idx.Sdelta = Convert.ToInt16(tracks[i].TrackIndex[1].Srel - tracks[i].TrackIndex[0].Srel);
|
||||
idx.Fdelta = Convert.ToInt16(tracks[i].TrackIndex[1].Frel - tracks[i].TrackIndex[0].Frel);
|
||||
|
||||
tracks[i].TrackIndex[0].Mdelta = idx.m;
|
||||
tracks[i].TrackIndex[0].Sdelta = idx.s;
|
||||
tracks[i].TrackIndex[0].Fdelta = idx.f;
|
||||
|
||||
// index is always ofset by 2 thou
|
||||
idx.Sdelta = 2;
|
||||
tracks[i].TrackIndex[1].Mdelta = idx.m;
|
||||
tracks[i].TrackIndex[1].Sdelta = idx.s;
|
||||
tracks[i].TrackIndex[1].Fdelta = idx.f;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CreateToc()
|
||||
{
|
||||
using (MemoryStream toc = new MemoryStream())
|
||||
{
|
||||
StreamUtil tocUtil = new StreamUtil(toc);
|
||||
tocUtil.WriteBytes(createDummyTracks());
|
||||
|
||||
for (int trackNo = 0; trackNo < tracks.Length; trackNo++)
|
||||
{
|
||||
if (tracks[trackNo] is not null)
|
||||
{
|
||||
tocUtil.WriteBytes(tracks[trackNo].ToTocEntry());
|
||||
}
|
||||
}
|
||||
|
||||
int remain = Convert.ToInt32(0x3F0 - toc.Length);
|
||||
tocUtil.WritePadding(0x00, remain);
|
||||
|
||||
toc.Seek(0x00, SeekOrigin.Begin);
|
||||
return toc.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach(CueStream openTrack in openTracks.Values)
|
||||
{
|
||||
if(openTrack.IsClosed)
|
||||
openTrack.Close();
|
||||
}
|
||||
openTracks.Clear();
|
||||
}
|
||||
|
||||
public CueReader(string cueFile)
|
||||
{
|
||||
openTracks = new Dictionary<int, CueStream>();
|
||||
for (int trackNo = 0; trackNo < tracks.Length; trackNo++) tracks[trackNo] = null;
|
||||
|
||||
using (TextReader cueReader = File.OpenText(cueFile))
|
||||
{
|
||||
CueTrack? curTrack = null;
|
||||
|
||||
for (string? cueData = cueReader.ReadLine();
|
||||
cueData != null;
|
||||
cueData = cueReader.ReadLine())
|
||||
{
|
||||
string[] cueLn = cueData.Trim().Replace("\r", "").Replace("\n", "").Split(' ');
|
||||
|
||||
if (cueData.StartsWith(" ")) // index of track
|
||||
{
|
||||
if (cueLn[0] == "INDEX")
|
||||
{
|
||||
if (curTrack is null) throw new Exception("tried to create new index, when track was null");
|
||||
|
||||
int indexNumber = Convert.ToByte(int.Parse(cueLn[1]));
|
||||
string[] msf = cueLn[2].Split(':');
|
||||
|
||||
curTrack.TrackIndex[indexNumber].Mrel = Convert.ToByte(Int32.Parse(msf[0]));
|
||||
curTrack.TrackIndex[indexNumber].Srel = Convert.ToByte(Int32.Parse(msf[1]));
|
||||
curTrack.TrackIndex[indexNumber].Frel = Convert.ToByte(Int32.Parse(msf[2]));
|
||||
|
||||
setTrackNumber(curTrack.TrackNo, ref curTrack);
|
||||
}
|
||||
}
|
||||
else if (cueData.StartsWith(" ")) // start of new track
|
||||
{
|
||||
if (cueLn[0] == "TRACK")
|
||||
{
|
||||
if (curTrack is null) throw new Exception("tried to create new track, when track was null");
|
||||
|
||||
if (curTrack.TrackNo != 0xFF)
|
||||
{
|
||||
setTrackNumber(curTrack.TrackNo, ref curTrack);
|
||||
curTrack = new CueTrack(curTrack.binFileName);
|
||||
}
|
||||
|
||||
curTrack.TrackNo = Convert.ToByte(int.Parse(cueLn[1]));
|
||||
if (cueLn[2] == "MODE2/2352")
|
||||
curTrack.TrackType = TrackType.TRACK_MODE2_2352;
|
||||
else if (cueLn[2] == "AUDIO")
|
||||
curTrack.TrackType = TrackType.TRACK_CDDA;
|
||||
setTrackNumber(curTrack.TrackNo, ref curTrack);
|
||||
}
|
||||
}
|
||||
else // new file
|
||||
{
|
||||
if (cueLn[0] == "FILE")
|
||||
{
|
||||
if (curTrack != null) setTrackNumber(curTrack.TrackNo, ref curTrack);
|
||||
|
||||
// parse out filename..
|
||||
string[] cueFnameParts = new string[cueLn.Length - 2];
|
||||
Array.ConstrainedCopy(cueLn, 1, cueFnameParts, 0, cueFnameParts.Length);
|
||||
string cueFname = String.Join(' ', cueFnameParts);
|
||||
|
||||
// open file ..
|
||||
string binFileName = cueFname.Substring(1, cueFname.Length - 2);
|
||||
string? folderContainingCue = Path.GetDirectoryName(cueFile);
|
||||
|
||||
if (folderContainingCue != null)
|
||||
binFileName = Path.Combine(folderContainingCue, binFileName);
|
||||
|
||||
curTrack = new CueTrack(binFileName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fixUpMsf();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
using Org.BouncyCastle.Tls.Crypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Cue
|
||||
{
|
||||
public class CueStream : Stream
|
||||
{
|
||||
internal long position;
|
||||
internal long start;
|
||||
internal long end;
|
||||
internal long length;
|
||||
public bool IsClosed;
|
||||
|
||||
private Stream baseStream;
|
||||
public CueStream(Stream s, long start, long length)
|
||||
{
|
||||
this.IsClosed = false;
|
||||
this.baseStream = s;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
this.end = start + length;
|
||||
|
||||
s.Seek(start, SeekOrigin.Begin);
|
||||
}
|
||||
private long remainLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Length - this.Position;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.baseStream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
return this.baseStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.baseStream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.position;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.position = value;
|
||||
if (this.position > this.end) this.position = this.end;
|
||||
if (this.position < 0) this.position = 0;
|
||||
|
||||
this.baseStream.Position = (start + this.position);
|
||||
}
|
||||
}
|
||||
private void seekToPos()
|
||||
{
|
||||
if (this.baseStream.Position != this.position)
|
||||
this.baseStream.Seek(start + this.position, SeekOrigin.Begin);
|
||||
}
|
||||
public override void Close()
|
||||
{
|
||||
IsClosed = true;
|
||||
this.baseStream.Dispose();
|
||||
base.Close();
|
||||
}
|
||||
public override void Flush()
|
||||
{
|
||||
this.baseStream.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
seekToPos();
|
||||
|
||||
int nCount = count;
|
||||
if (nCount > remainLength) nCount = Convert.ToInt32(remainLength);
|
||||
if (nCount < 0) nCount = 0;
|
||||
|
||||
int read = this.baseStream.Read(buffer, offset, count);
|
||||
this.position += read;
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
default:
|
||||
case SeekOrigin.Begin:
|
||||
this.Position = offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
this.Position += offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
this.Position = this.Length - offset;
|
||||
break;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException("Cannot set length of CueStream.");
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
seekToPos();
|
||||
|
||||
int nCount = count;
|
||||
if (nCount > remainLength) nCount = Convert.ToInt32(remainLength);
|
||||
if (nCount < 0) nCount = 0;
|
||||
|
||||
this.baseStream.Write(buffer, offset, count);
|
||||
this.position += nCount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Cue
|
||||
{
|
||||
public class CueTrack
|
||||
{
|
||||
|
||||
public const int MODE2_SECTOR_SZ = 2352;
|
||||
public const int CDDA_SECTOR_SZ = 2352;
|
||||
|
||||
public TrackType TrackType;
|
||||
public byte TrackNo;
|
||||
public CueIndex[] TrackIndex;
|
||||
|
||||
public int TrackLength;
|
||||
public int SectorSz
|
||||
{
|
||||
get
|
||||
{
|
||||
if (TrackType == TrackType.TRACK_CDDA) return CDDA_SECTOR_SZ;
|
||||
else return MODE2_SECTOR_SZ;
|
||||
}
|
||||
}
|
||||
internal long binFileSz;
|
||||
internal string binFileName;
|
||||
|
||||
internal CueTrack(string binFile)
|
||||
{
|
||||
TrackIndex = new CueIndex[2];
|
||||
for (int i = 0; i < TrackIndex.Length; i++)
|
||||
TrackIndex[i] = new CueIndex(Convert.ToByte(i));
|
||||
|
||||
binFileName = binFile;
|
||||
binFileSz = new FileInfo(binFileName).Length;
|
||||
|
||||
TrackType = TrackType.TRACK_MODE2_2352;
|
||||
TrackNo = 0xFF;
|
||||
}
|
||||
|
||||
public byte[] ToTocEntry()
|
||||
{
|
||||
byte[] tocEntry = new byte[10];
|
||||
|
||||
tocEntry[0] = Convert.ToByte(this.TrackType);
|
||||
tocEntry[1] = 0;
|
||||
tocEntry[2] = CueReader.BinaryDecimalConv(this.TrackNo);
|
||||
|
||||
|
||||
tocEntry[3] = this.TrackIndex[0].M;
|
||||
tocEntry[4] = this.TrackIndex[0].S;
|
||||
tocEntry[5] = this.TrackIndex[0].F;
|
||||
tocEntry[6] = 0;
|
||||
tocEntry[7] = this.TrackIndex[1].M;
|
||||
tocEntry[8] = this.TrackIndex[1].S;
|
||||
tocEntry[9] = this.TrackIndex[1].F;
|
||||
|
||||
return tocEntry;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Cue
|
||||
{
|
||||
public enum TrackType
|
||||
{
|
||||
TRACK_MODE2_2352 = 0x41,
|
||||
TRACK_CDDA = 0x01
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
using PopsBuilder.Atrac3;
|
||||
using PopsBuilder.Cue;
|
||||
using PopsBuilder.Psp;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Pops
|
||||
{
|
||||
public class DiscCompressor
|
||||
{
|
||||
const int COMPRESS_BLOCK_SZ = 0x9300;
|
||||
const int DEFAULT_ISO_OFFSET = 0x100000;
|
||||
public int IsoOffset;
|
||||
|
||||
internal DiscCompressor(NpDrmPsar srcImg, DiscInfo disc, IAtracEncoderBase encoder, int offset = DEFAULT_ISO_OFFSET)
|
||||
{
|
||||
this.srcImg = srcImg;
|
||||
this.disc = disc;
|
||||
this.cue = new CueReader(disc.CueFile);
|
||||
|
||||
this.IsoHeader = new MemoryStream();
|
||||
this.CompressedIso = new MemoryStream();
|
||||
|
||||
this.isoHeaderUtil = new StreamUtil(IsoHeader);
|
||||
this.atrac3Encoder = encoder;
|
||||
this.IsoOffset = offset;
|
||||
}
|
||||
|
||||
|
||||
private void writeCompressedIsoBlock(Stream s)
|
||||
{
|
||||
byte[] isoBlock = new byte[COMPRESS_BLOCK_SZ];
|
||||
int read = s.Read(isoBlock, 0, isoBlock.Length);
|
||||
|
||||
byte[] compressed = Lz.compress(isoBlock);
|
||||
|
||||
ushort sz = Convert.ToUInt16(compressed.Length);
|
||||
int ptr = Convert.ToInt32(CompressedIso.Position);
|
||||
writeIsoTblEntry(ptr, sz, compressed);
|
||||
|
||||
CompressedIso.Write(compressed, 0, compressed.Length);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void writeIsoTblEntry(int ptr, ushort sz, byte[] data)
|
||||
{
|
||||
isoHeaderUtil.WriteInt32(ptr);
|
||||
isoHeaderUtil.WriteUInt16(sz);
|
||||
|
||||
isoHeaderUtil.WriteInt16(1); // mark that this is part of the image.
|
||||
|
||||
isoHeaderUtil.WriteBytes(calculatePs1CompressedIsoSegmentChecksum(data));
|
||||
|
||||
isoHeaderUtil.WritePadding(0x00, 0x8);
|
||||
}
|
||||
|
||||
|
||||
private void writeHeader()
|
||||
{
|
||||
isoHeaderUtil.WriteStrWithPadding(disc.DiscIdHdr, 0x00, 0x400);
|
||||
}
|
||||
|
||||
|
||||
public byte[] GenerateIsoPgd()
|
||||
{
|
||||
IsoHeader.Seek(0x0, SeekOrigin.Begin);
|
||||
byte[] isoHdr = IsoHeader.ToArray();
|
||||
|
||||
int headerSize = DNASHelper.CalculateSize(isoHdr.Length, 0x400);
|
||||
byte[] headerEnc = new byte[headerSize];
|
||||
|
||||
int sz = DNASHelper.Encrypt(headerEnc, isoHdr, srcImg.VersionKey, isoHdr.Length, 1, 1, blockSize: 0x400);
|
||||
byte[] isoHdrPgd = headerEnc.ToArray();
|
||||
Array.Resize(ref isoHdrPgd, sz);
|
||||
|
||||
return isoHdrPgd;
|
||||
}
|
||||
|
||||
private void writeIsoLocation()
|
||||
{
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
|
||||
isoHeaderUtil.WriteInt32(IsoOffset); // always 0x100000 on single disc game
|
||||
|
||||
isoHeaderUtil.WritePadding(0x00, 0x620);
|
||||
}
|
||||
|
||||
private void writeCompressedIso()
|
||||
{
|
||||
using (CueStream cueStr = cue.OpenTrack(cue.FirstDataTrackNo))
|
||||
{
|
||||
using (EccRemoverStream eccRem = new EccRemoverStream(cueStr))
|
||||
{
|
||||
while (eccRem.Position < eccRem.Length)
|
||||
{
|
||||
Console.Write(Math.Floor(Convert.ToDouble(eccRem.Position) / Convert.ToDouble(eccRem.Length) * 100.0) + "%\r");
|
||||
writeCompressedIsoBlock(eccRem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void GenerateIsoHeaderAndCompress()
|
||||
{
|
||||
writeHeader();
|
||||
writeTOC();
|
||||
writeIsoLocation();
|
||||
writeName();
|
||||
|
||||
writeCompressedIso();
|
||||
|
||||
isoHeaderUtil.PadUntil(0x0, 0xb3880);
|
||||
|
||||
// now write CD-Audio data.
|
||||
writeCompressedCDATracks();
|
||||
}
|
||||
|
||||
public void WriteSimpleDatLocation(Int64 location)
|
||||
{
|
||||
IsoHeader.Seek(0xE20, SeekOrigin.Begin);
|
||||
isoHeaderUtil.WriteInt64(location);
|
||||
}
|
||||
private void writeName()
|
||||
{
|
||||
// copied from crash bandicoot warped
|
||||
|
||||
isoHeaderUtil.WriteInt64(0x00); // SIMPLE.DAT location
|
||||
|
||||
isoHeaderUtil.WriteInt32(2047); // unk
|
||||
isoHeaderUtil.WriteStrWithPadding(disc.DiscName, 0x00, 0x80);
|
||||
isoHeaderUtil.WriteInt32(3); // unk
|
||||
|
||||
isoHeaderUtil.WriteInt32(0x72d0ee59); // appears to be constant?
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
|
||||
isoHeaderUtil.WritePadding(0, 0x2D40);
|
||||
}
|
||||
|
||||
private void writeCDAEntry(int position, int length, uint key)
|
||||
{
|
||||
isoHeaderUtil.WriteInt32(position);
|
||||
isoHeaderUtil.WriteInt32(length);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteUInt32(key);
|
||||
}
|
||||
|
||||
private void writeCompressedCDATracks()
|
||||
{
|
||||
Random rng = new Random();
|
||||
|
||||
IsoHeader.Seek(0x800, SeekOrigin.Begin); // CDA Entries
|
||||
|
||||
int totalTracks = cue.GetTotalTracks();
|
||||
for (int i = 1; i <= totalTracks; i++)
|
||||
{
|
||||
if (cue.GetTrackNumber(i).TrackType != TrackType.TRACK_CDDA) continue;
|
||||
|
||||
Console.WriteLine("Encoding track " + i + " to ATRAC3.");
|
||||
|
||||
using (CueStream audioStream = cue.OpenTrack(i))
|
||||
{
|
||||
uint key = Convert.ToUInt32(rng.NextInt64(0, uint.MaxValue));
|
||||
|
||||
Atrac3ToolEncoder enc = new Atrac3ToolEncoder();
|
||||
|
||||
byte[] pcmData = new byte[audioStream.Length];
|
||||
audioStream.Read(pcmData, 0x00, pcmData.Length);
|
||||
|
||||
byte[] atracData = enc.EncodeToAtrac(pcmData);
|
||||
|
||||
writeCDAEntry(Convert.ToInt32(CompressedIso.Position), atracData.Length, key);
|
||||
|
||||
using (MemoryStream atracStream = new MemoryStream(atracData))
|
||||
{
|
||||
using (MemoryStream encryptedAtracStream = new MemoryStream())
|
||||
{
|
||||
AtracCrypto.ScrambleAtracData(atracStream, encryptedAtracStream, key);
|
||||
encryptedAtracStream.Seek(0x00, SeekOrigin.Begin);
|
||||
encryptedAtracStream.CopyTo(CompressedIso);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] calculatePs1CompressedIsoSegmentChecksum(byte[] data)
|
||||
{
|
||||
byte[] outChecksum = new byte[0x10];
|
||||
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<AMCTRL.MAC_KEY>()];
|
||||
|
||||
AMCTRL.sceDrmBBMacInit(mkey, 3);
|
||||
AMCTRL.sceDrmBBMacUpdate(mkey, data, data.Length);
|
||||
Span<byte> checksum = new byte[20 + 0x10];
|
||||
AMCTRL.sceDrmBBMacFinal(mkey, checksum[20..], srcImg.VersionKey);
|
||||
|
||||
ref var aesHdr = ref MemoryMarshal.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(checksum);
|
||||
aesHdr.mode = KIRKEngine.KIRK_MODE_ENCRYPT_CBC;
|
||||
aesHdr.keyseed = 0x63;
|
||||
aesHdr.data_size = 0x10;
|
||||
KIRKEngine.sceUtilsBufferCopyWithRange(checksum, 0x10, checksum, 0x10, KIRKEngine.KIRK_CMD_ENCRYPT_IV_0);
|
||||
|
||||
checksum.Slice(20, 0x10).CopyTo(outChecksum);
|
||||
|
||||
return outChecksum;
|
||||
}
|
||||
|
||||
private void writeTOC()
|
||||
{
|
||||
isoHeaderUtil.WriteBytes(cue.CreateToc());
|
||||
}
|
||||
|
||||
|
||||
private DiscInfo disc;
|
||||
private CueReader cue;
|
||||
private NpDrmPsar srcImg;
|
||||
|
||||
public MemoryStream IsoHeader;
|
||||
public MemoryStream CompressedIso;
|
||||
|
||||
private StreamUtil isoHeaderUtil;
|
||||
private IAtracEncoderBase atrac3Encoder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Pops
|
||||
{
|
||||
public class DiscInfo
|
||||
{
|
||||
private string cueFile;
|
||||
private string discName;
|
||||
private string discId;
|
||||
|
||||
public string CueFile
|
||||
{ get
|
||||
{
|
||||
return cueFile;
|
||||
}
|
||||
}
|
||||
|
||||
public string DiscIdHdr
|
||||
{
|
||||
get
|
||||
{
|
||||
return "_" + DiscId.Substring(0, 4) + "_" + DiscId.Substring(4, 5);
|
||||
}
|
||||
}
|
||||
public string DiscName
|
||||
{
|
||||
get
|
||||
{
|
||||
return discName;
|
||||
}
|
||||
}
|
||||
|
||||
public string DiscId
|
||||
{
|
||||
get
|
||||
{
|
||||
return discId.Replace("-", "").Replace("_", "").ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public DiscInfo(string cueFile, string discName, string discId)
|
||||
{
|
||||
this.cueFile = cueFile;
|
||||
this.discName = discName;
|
||||
this.discId = discId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
using PopsBuilder.Cue;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Pops
|
||||
{
|
||||
public class EccRemoverStream : Stream
|
||||
{
|
||||
private byte[] currentSector;
|
||||
|
||||
private long position;
|
||||
private Stream baseStream;
|
||||
public EccRemoverStream(Stream s)
|
||||
{
|
||||
baseStream = s;
|
||||
currentSector = new byte[CueTrack.MODE2_SECTOR_SZ];
|
||||
|
||||
invalidateSectorCache();
|
||||
}
|
||||
private int positionInSector
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToInt32(position % CueTrack.MODE2_SECTOR_SZ);
|
||||
}
|
||||
}
|
||||
private int remainInSector
|
||||
{
|
||||
get
|
||||
{
|
||||
return CueTrack.MODE2_SECTOR_SZ - positionInSector;
|
||||
}
|
||||
}
|
||||
private int positionSector
|
||||
{
|
||||
get
|
||||
{
|
||||
return findSector(position);
|
||||
}
|
||||
}
|
||||
public Stream BaseStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream;
|
||||
}
|
||||
}
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
long newPos = value;
|
||||
|
||||
if (newPos < 0) newPos = 0;
|
||||
if (newPos > Length) newPos = Length;
|
||||
|
||||
|
||||
int oldSector = positionSector;
|
||||
position = newPos;
|
||||
|
||||
if (positionSector != oldSector)
|
||||
invalidateSectorCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void removeEcc()
|
||||
{
|
||||
// clear current sync
|
||||
Array.Fill(currentSector, (byte)0x00, 0x1, 0x0A);
|
||||
|
||||
// remove MSF ..
|
||||
currentSector[0x0C] = 0x00; // M
|
||||
currentSector[0x0D] = 0x00; // S
|
||||
currentSector[0x0E] = 0x00; // F
|
||||
|
||||
// remove ecc
|
||||
|
||||
// (only if this is not form2mode2 sector!)
|
||||
if (!(currentSector[0xF] == 0x2 && (currentSector[0x12] & 0x20) == 0x20))
|
||||
Array.Fill(currentSector, (byte)0x00, 0x818, 0x118);
|
||||
else if (position > 0x9300) // only clear if its past the system section ..
|
||||
Array.Fill(currentSector, (byte)0x00, 0x92C, 0x4);
|
||||
}
|
||||
|
||||
private int findSector(long position)
|
||||
{
|
||||
long len = position;
|
||||
len -= len % CueTrack.MODE2_SECTOR_SZ;
|
||||
int sector = Convert.ToInt32(len / CueTrack.MODE2_SECTOR_SZ);
|
||||
return sector;
|
||||
}
|
||||
|
||||
private long sectorToPos(int sector)
|
||||
{
|
||||
return sector * CueTrack.MODE2_SECTOR_SZ;
|
||||
}
|
||||
|
||||
private void seekToSector(int sector)
|
||||
{
|
||||
baseStream.Seek(sectorToPos(sector), SeekOrigin.Begin);
|
||||
}
|
||||
private void invalidateSectorCache()
|
||||
{
|
||||
|
||||
int sector = findSector(position);
|
||||
seekToSector(sector);
|
||||
baseStream.Read(currentSector, 0x00, currentSector.Length);
|
||||
removeEcc();
|
||||
|
||||
}
|
||||
public override void Close()
|
||||
{
|
||||
baseStream.Close();
|
||||
base.Close();
|
||||
}
|
||||
public override void Flush()
|
||||
{
|
||||
baseStream.Flush();
|
||||
}
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int effectiveCount = count;
|
||||
|
||||
if (Position > Length) return 0;
|
||||
|
||||
if (Position + effectiveCount > Length) effectiveCount = Convert.ToInt32(Length - Position);
|
||||
|
||||
if (effectiveCount <= remainInSector) // read the data from the cached sector
|
||||
{
|
||||
Array.ConstrainedCopy(currentSector, positionInSector, buffer, offset, effectiveCount);
|
||||
}
|
||||
else if (effectiveCount > remainInSector) // read 1 sector at a time until count reached
|
||||
{
|
||||
int remain = effectiveCount;
|
||||
int total = 0;
|
||||
|
||||
while (remain > 0)
|
||||
{
|
||||
int toRead = Math.Min(remain, remainInSector);
|
||||
int totalRead = Read(buffer, total + offset, toRead);
|
||||
|
||||
if (totalRead < toRead) break;
|
||||
|
||||
remain -= totalRead;
|
||||
total += totalRead;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
Position += effectiveCount;
|
||||
return effectiveCount;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
default:
|
||||
case SeekOrigin.Begin:
|
||||
Position = offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
Position += offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
Position = Length - offset;
|
||||
break;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException("EccRemoverStream is read only.");
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException("EccRemoverStream is read only.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
using Org.BouncyCastle.Crypto.Paddings;
|
||||
using PopsBuilder.Psp;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Pops
|
||||
{
|
||||
public class PopsImg : NpDrmPsar
|
||||
{
|
||||
public PopsImg(byte[] versionKey, string contentId) : base(versionKey, contentId)
|
||||
{
|
||||
startDat = new MemoryStream();
|
||||
startDatUtil = new StreamUtil(startDat);
|
||||
|
||||
simple = new MemoryStream();
|
||||
simpleUtil = new StreamUtil(simple);
|
||||
|
||||
createStartDat();
|
||||
createSimpleDat();
|
||||
|
||||
SimplePgd = generateSimplePgd();
|
||||
|
||||
}
|
||||
internal MemoryStream startDat;
|
||||
internal StreamUtil startDatUtil;
|
||||
|
||||
private MemoryStream simple;
|
||||
private StreamUtil simpleUtil;
|
||||
public byte[] SimplePgd;
|
||||
internal Random rng = new Random();
|
||||
private void createSimpleDat()
|
||||
{
|
||||
simpleUtil.WriteStr("SIMPLE ");
|
||||
simpleUtil.WriteInt32(100);
|
||||
simpleUtil.WriteInt32(16);
|
||||
simpleUtil.WriteInt32(Resources.SIMPLE.Length);
|
||||
simpleUtil.WriteInt32(0);
|
||||
simpleUtil.WriteInt32(0);
|
||||
|
||||
simpleUtil.WriteBytes(Resources.SIMPLE);
|
||||
}
|
||||
|
||||
private void createStartDat()
|
||||
{
|
||||
startDatUtil.WriteStr("STARTDAT");
|
||||
startDatUtil.WriteInt32(0x1);
|
||||
startDatUtil.WriteInt32(0x1);
|
||||
startDatUtil.WriteInt32(0x50);
|
||||
startDatUtil.WriteInt32(Resources.STARTDAT.Length);
|
||||
startDatUtil.WriteInt32(0x0);
|
||||
startDatUtil.WriteInt32(0x0);
|
||||
|
||||
startDatUtil.WritePadding(0, 0x30);
|
||||
|
||||
startDatUtil.WriteBytes(Resources.STARTDAT);
|
||||
}
|
||||
|
||||
private byte[] generateSimplePgd()
|
||||
{
|
||||
simple.Seek(0x0, SeekOrigin.Begin);
|
||||
byte[] simpleData = simple.ToArray();
|
||||
|
||||
int simpleSz = DNASHelper.CalculateSize(simpleData.Length, 0x400);
|
||||
byte[] simpleEnc = new byte[simpleSz];
|
||||
|
||||
// get pgd
|
||||
int sz = DNASHelper.Encrypt(simpleEnc, simpleData, VersionKey, simpleData.Length, 1, 1, blockSize: 0x400);
|
||||
byte[] pgd = simpleEnc.ToArray();
|
||||
Array.Resize(ref pgd, sz);
|
||||
|
||||
return pgd;
|
||||
}
|
||||
|
||||
|
||||
public byte[] GenerateDataPsp()
|
||||
{
|
||||
Span<byte> loaderEnc = new byte[0x9B13];
|
||||
|
||||
byte[] dataPspElf = Resources.DATAPSPSD;
|
||||
|
||||
// calculate size low and high part ..
|
||||
uint szLow = Convert.ToUInt32(Psar.Length) >> 16;
|
||||
uint szHigh = Convert.ToUInt32(Psar.Length) & 0xFFFF;
|
||||
|
||||
// convert to big endain bytes
|
||||
byte[] lowBits = BitConverter.GetBytes(Convert.ToUInt16(szLow)).ToArray();
|
||||
byte[] highBits = BitConverter.GetBytes(Convert.ToUInt16(szHigh)).ToArray();
|
||||
|
||||
// overwrite data.psar size check ..
|
||||
Array.ConstrainedCopy(lowBits, 0, dataPspElf, 0x68C, 0x2);
|
||||
Array.ConstrainedCopy(highBits, 0, dataPspElf, 0x694, 0x2);
|
||||
|
||||
SceMesgLed.Encrypt(loaderEnc, dataPspElf, 0x0DAA06F0, SceExecFileDecryptMode.DECRYPT_MODE_POPS_EXEC, VersionKey, ContentId, Resources.DATAPSPSDCFG);
|
||||
return loaderEnc.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using Org.BouncyCastle.Crypto.Paddings;
|
||||
using PopsBuilder.Atrac3;
|
||||
using PopsBuilder.Cue;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace PopsBuilder.Pops
|
||||
{
|
||||
public class PsIsoImg : PopsImg
|
||||
{
|
||||
internal PsIsoImg(byte[] versionkey, string contentId, DiscCompressor discCompressor) : base(versionkey, contentId)
|
||||
{
|
||||
this.compressor = discCompressor;
|
||||
}
|
||||
|
||||
public PsIsoImg(byte[] versionkey, string contentId, DiscInfo disc, IAtracEncoderBase encoder) : base(versionkey, contentId)
|
||||
{
|
||||
this.compressor = new DiscCompressor(this, disc, encoder);
|
||||
}
|
||||
|
||||
public PsIsoImg(byte[] versionkey, string contentId, DiscInfo disc) : base(versionkey, contentId)
|
||||
{
|
||||
this.compressor = new DiscCompressor(this, disc, new Atrac3ToolEncoder());
|
||||
}
|
||||
public void CreatePsar(bool isPartOfMultiDisc=false)
|
||||
{
|
||||
compressor.GenerateIsoHeaderAndCompress();
|
||||
if (!isPartOfMultiDisc) compressor.WriteSimpleDatLocation((compressor.IsoOffset + compressor.CompressedIso.Length) + startDat.Length);
|
||||
|
||||
psarUtil.WriteStr("PSISOIMG0000");
|
||||
psarUtil.WriteInt64(0x00); // location of STARTDAT
|
||||
|
||||
psarUtil.WritePadding(0x00, 0x3ec); // Skip forwards
|
||||
|
||||
byte[] isoHdrPgd = compressor.GenerateIsoPgd();
|
||||
psarUtil.WriteBytes(isoHdrPgd);
|
||||
psarUtil.PadUntil(0x00, compressor.IsoOffset);
|
||||
|
||||
compressor.CompressedIso.Seek(0x00, SeekOrigin.Begin);
|
||||
compressor.CompressedIso.CopyTo(Psar);
|
||||
|
||||
Psar.Seek(0x00, SeekOrigin.Begin);
|
||||
if (isPartOfMultiDisc) return;
|
||||
|
||||
// write STARTDAT
|
||||
Int64 startDatLocation = Psar.Position;
|
||||
startDat.Seek(0x00, SeekOrigin.Begin);
|
||||
startDat.CopyTo(Psar);
|
||||
|
||||
// write pgd
|
||||
psarUtil.WriteBytes(this.SimplePgd);
|
||||
|
||||
// set STARTDAT location
|
||||
Psar.Seek(0xC, SeekOrigin.Begin);
|
||||
psarUtil.WriteInt64(startDatLocation);
|
||||
|
||||
Psar.Seek(0x00, SeekOrigin.Begin);
|
||||
}
|
||||
private DiscCompressor compressor;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
using PopsBuilder.Atrac3;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Pops
|
||||
{
|
||||
public class PsTitleImg : PopsImg
|
||||
{
|
||||
const int MAX_DISCS = 5;
|
||||
const int PSISO_ALIGN = 0x8000;
|
||||
public PsTitleImg(byte[] versionKey, string contentId, DiscInfo[] discs) : base(versionKey, contentId)
|
||||
{
|
||||
if (discs.Length > MAX_DISCS) throw new Exception("Sorry, multi disc games only support up to 5 discs... (i dont make the rules)");
|
||||
this.compressors = new DiscCompressor[MAX_DISCS];
|
||||
this.discs = discs;
|
||||
|
||||
for (int i = 0; i < compressors.Length; i++)
|
||||
{
|
||||
if (i > (discs.Length - 1)) compressors[i] = null;
|
||||
else compressors[i] = new DiscCompressor(this, discs[i], new Atrac3ToolEncoder());
|
||||
}
|
||||
|
||||
|
||||
isoMap = new MemoryStream();
|
||||
isoMapUtil = new StreamUtil(isoMap);
|
||||
|
||||
isoPart = new MemoryStream();
|
||||
isoPartUtil = new StreamUtil(isoPart);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void CreatePsar()
|
||||
{
|
||||
createIsoMap();
|
||||
|
||||
psarUtil.WriteStr("PSTITLEIMG000000");
|
||||
psarUtil.WriteInt64(PSISO_ALIGN+isoPart.Length); // location of STARTDAT
|
||||
|
||||
psarUtil.WriteRandom(0x10); // dunno what this is
|
||||
psarUtil.WritePadding(0x00, 0x1D8);
|
||||
|
||||
byte[] isoMap = generateIsoMapPgd();
|
||||
psarUtil.WriteBytes(isoMap);
|
||||
psarUtil.PadUntil(0x00, PSISO_ALIGN);
|
||||
|
||||
isoPart.Seek(0x00, SeekOrigin.Begin);
|
||||
isoPart.CopyTo(Psar);
|
||||
|
||||
startDat.Seek(0x00, SeekOrigin.Begin);
|
||||
startDat.CopyTo(Psar);
|
||||
|
||||
psarUtil.WriteBytes(SimplePgd);
|
||||
}
|
||||
|
||||
private byte[] calculateChecksumForIsoImgTitle(byte[] header)
|
||||
{
|
||||
byte[] checksum = new byte[0x10];
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<PspCrypto.AMCTRL.MAC_KEY>()];
|
||||
|
||||
PspCrypto.AMCTRL.sceDrmBBMacInit(mkey, 3);
|
||||
PspCrypto.AMCTRL.sceDrmBBMacUpdate(mkey, header, header.Length /*0xb3c80*/);
|
||||
Span<byte> newKey = new byte[20 + 0x10];
|
||||
PspCrypto.AMCTRL.sceDrmBBMacFinal(mkey, newKey[20..], VersionKey);
|
||||
ref var aesHdr = ref MemoryMarshal.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(newKey);
|
||||
aesHdr.mode = KIRKEngine.KIRK_MODE_ENCRYPT_CBC;
|
||||
aesHdr.keyseed = 0x63;
|
||||
aesHdr.data_size = 0x10;
|
||||
PspCrypto.KIRKEngine.sceUtilsBufferCopyWithRange(newKey, 0x10, newKey, 0x10, KIRKEngine.KIRK_CMD_ENCRYPT_IV_0);
|
||||
|
||||
newKey.Slice(20, 0x10).CopyTo(checksum);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
private byte[] generateIsoMapPgd()
|
||||
{
|
||||
isoMap.Seek(0, SeekOrigin.Begin);
|
||||
byte[] isoMapBuf = isoMap.ToArray();
|
||||
|
||||
int encryptedSz = DNASHelper.CalculateSize(isoMapBuf.Length, 1024);
|
||||
var isoMapEnc = new byte[encryptedSz];
|
||||
|
||||
DNASHelper.Encrypt(isoMapEnc, isoMapBuf, VersionKey, isoMapBuf.Length, 1, 1);
|
||||
|
||||
return isoMapEnc;
|
||||
}
|
||||
|
||||
private void createIsoMap()
|
||||
{
|
||||
byte[] checksums = new byte[0x10 * MAX_DISCS];
|
||||
for(int i = 0; i < MAX_DISCS; i++)
|
||||
{
|
||||
if (compressors[i] is null) { isoMapUtil.WriteInt32(0); continue; };
|
||||
|
||||
int padLen = Convert.ToInt32(PSISO_ALIGN - (isoPart.Position % PSISO_ALIGN));
|
||||
isoPartUtil.WritePadding(0x00, padLen);
|
||||
|
||||
using (PsIsoImg psIsoImg = new PsIsoImg(this.VersionKey, this.ContentId, compressors[i]))
|
||||
{
|
||||
isoMapUtil.WriteUInt32(Convert.ToUInt32(PSISO_ALIGN + isoPart.Position));
|
||||
|
||||
psIsoImg.CreatePsar(true);
|
||||
|
||||
|
||||
psIsoImg.Psar.Seek(0x0, SeekOrigin.Begin);
|
||||
compressors[i].IsoHeader.Seek(0x00, SeekOrigin.Begin);
|
||||
|
||||
byte[] isoHdr = new byte[compressors[i].IsoHeader.Length + 0x400];
|
||||
psIsoImg.Psar.Read(isoHdr, 0x00, 0x400);
|
||||
compressors[i].IsoHeader.Read(isoHdr, 0x400, Convert.ToInt32(compressors[i].IsoHeader.Length));
|
||||
|
||||
byte[] checksum = calculateChecksumForIsoImgTitle(isoHdr);
|
||||
Array.ConstrainedCopy(checksum, 0, checksums, i * 0x10, 0x10);
|
||||
|
||||
psIsoImg.Psar.Seek(0x00, SeekOrigin.Begin);
|
||||
psIsoImg.Psar.CopyTo(isoPart);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
isoMapUtil.WriteBytes(checksums);
|
||||
isoMapUtil.WriteStrWithPadding(discs.First().DiscIdHdr, 0x00, 0x20);
|
||||
|
||||
isoMapUtil.WriteInt64(Convert.ToInt64(PSISO_ALIGN + isoPart.Length + startDat.Length));
|
||||
isoMapUtil.WriteRandom(0x80);
|
||||
isoMapUtil.WriteStrWithPadding(discs.First().DiscName, 0x00, 0x80);
|
||||
isoMapUtil.WriteInt32(MAX_DISCS);
|
||||
isoMapUtil.WritePadding(0x00, 0x70);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
isoPart.Dispose();
|
||||
isoMap.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
private DiscInfo[] discs;
|
||||
private DiscCompressor[] compressors;
|
||||
|
||||
private MemoryStream isoPart;
|
||||
private StreamUtil isoPartUtil;
|
||||
|
||||
private MemoryStream isoMap;
|
||||
private StreamUtil isoMapUtil;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PspCrypto\PspCrypto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,34 @@
|
|||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Psp
|
||||
{
|
||||
public class NpDrmPsar : IDisposable
|
||||
{
|
||||
public NpDrmPsar(byte[] versionKey, string contentId)
|
||||
{
|
||||
VersionKey = versionKey;
|
||||
ContentId = contentId;
|
||||
|
||||
Psar = new MemoryStream();
|
||||
psarUtil = new StreamUtil(Psar);
|
||||
}
|
||||
|
||||
public byte[] VersionKey;
|
||||
public string ContentId;
|
||||
|
||||
public MemoryStream Psar;
|
||||
internal StreamUtil psarUtil;
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Psar.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder.Psp
|
||||
{
|
||||
public static class PbpBuilder
|
||||
{
|
||||
public static void CreatePbp(byte[]? paramSfo, byte[]? icon0Png, byte[]? icon1Png,
|
||||
byte[]? pic0Png, byte[]? pic1Png, byte[]? snd0At3,
|
||||
byte[] dataPsp, NpDrmPsar dataPsar, string outputFile)
|
||||
{
|
||||
|
||||
using (FileStream pbpStream = File.Open(outputFile, FileMode.Create))
|
||||
{
|
||||
StreamUtil pbpUtil = new StreamUtil(pbpStream);
|
||||
pbpUtil.WriteByte(0x00);
|
||||
pbpUtil.WriteStrWithPadding("PBP", 0x00, 0x5);
|
||||
pbpUtil.WriteInt16(1);
|
||||
|
||||
// param location
|
||||
uint loc = 0x28;
|
||||
if (paramSfo is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(paramSfo.Length); }
|
||||
|
||||
// icon0 location
|
||||
if (icon0Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(icon0Png.Length); }
|
||||
|
||||
// icon1 location
|
||||
if (icon1Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(icon1Png.Length); }
|
||||
|
||||
// pic0 location
|
||||
if (pic0Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(pic0Png.Length); }
|
||||
|
||||
// pic1 location
|
||||
if (pic1Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(pic1Png.Length); }
|
||||
|
||||
// snd0 location
|
||||
if (snd0At3 is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(snd0At3.Length); }
|
||||
|
||||
// datapsp location
|
||||
pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(dataPsp.Length);
|
||||
|
||||
// psar location
|
||||
pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(dataPsar.Psar.Length);
|
||||
|
||||
// write pbp metadata
|
||||
if (paramSfo is not null) pbpUtil.WriteBytes(paramSfo);
|
||||
if (icon0Png is not null) pbpUtil.WriteBytes(icon0Png);
|
||||
if (icon1Png is not null) pbpUtil.WriteBytes(icon1Png);
|
||||
if (pic0Png is not null) pbpUtil.WriteBytes(pic0Png);
|
||||
if (pic1Png is not null) pbpUtil.WriteBytes(pic1Png);
|
||||
if (snd0At3 is not null) pbpUtil.WriteBytes(snd0At3);
|
||||
|
||||
// write DATA.PSP
|
||||
pbpUtil.WriteBytes(dataPsp);
|
||||
|
||||
// write DATA.PSAR
|
||||
dataPsar.Psar.Seek(0x00, SeekOrigin.Begin);
|
||||
dataPsar.Psar.CopyTo(pbpStream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PopsBuilder {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PopsBuilder.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] DATAPSPSD {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("DATAPSPSD", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] DATAPSPSDCFG {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("DATAPSPSDCFG", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] SIMPLE {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("SIMPLE", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] STARTDAT {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("STARTDAT", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="DATAPSPSD" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\DATAPSPSD.ELF;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="DATAPSPSDCFG" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\DATAPSPSDCFG.BIN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="SIMPLE" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\SIMPLE.PNG;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="STARTDAT" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\STARTDAT.PNG;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder
|
||||
{
|
||||
public class StreamUtil
|
||||
{
|
||||
private Stream s;
|
||||
private Random rng;
|
||||
public StreamUtil(Stream s)
|
||||
{
|
||||
this.s = s;
|
||||
rng = new Random();
|
||||
}
|
||||
|
||||
public void WriteStrWithPadding(string str, byte b, int len)
|
||||
{
|
||||
byte[] sdata = Encoding.UTF8.GetBytes(str);
|
||||
if (len < sdata.Length)
|
||||
{
|
||||
s.Write(sdata, 0, len);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteBytes(sdata);
|
||||
WritePadding(b, (len - sdata.Length));
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteRandom(int len)
|
||||
{
|
||||
byte[] randomBytes = new byte[len];
|
||||
rng.NextBytes(randomBytes);
|
||||
this.WriteBytes(randomBytes);
|
||||
}
|
||||
public byte[] ReadBytes(int len)
|
||||
{
|
||||
byte[] data = new byte[len];
|
||||
s.Read(data, 0x00, len);
|
||||
return data;
|
||||
}
|
||||
public UInt16 ReadUInt16()
|
||||
{
|
||||
byte[] vbytes = ReadBytes(0x2);
|
||||
return BitConverter.ToUInt16(vbytes);
|
||||
}
|
||||
public Int16 ReadInt16()
|
||||
{
|
||||
byte[] vbytes = ReadBytes(0x2);
|
||||
return BitConverter.ToInt16(vbytes);
|
||||
}
|
||||
public UInt32 ReadUInt32()
|
||||
{
|
||||
byte[] vbytes = ReadBytes(0x4);
|
||||
return BitConverter.ToUInt32(vbytes);
|
||||
}
|
||||
public Int32 ReadInt32()
|
||||
{
|
||||
byte[] vbytes = ReadBytes(0x4);
|
||||
return BitConverter.ToInt32(vbytes);
|
||||
}
|
||||
public void WriteInt64(Int64 v)
|
||||
{
|
||||
WriteBytes(BitConverter.GetBytes(v));
|
||||
}
|
||||
public void WriteUInt16(UInt16 v)
|
||||
{
|
||||
WriteBytes(BitConverter.GetBytes(v));
|
||||
}
|
||||
public void WriteInt16(Int16 v)
|
||||
{
|
||||
WriteBytes(BitConverter.GetBytes(v));
|
||||
}
|
||||
|
||||
public void WriteUInt32(UInt32 v)
|
||||
{
|
||||
WriteBytes(BitConverter.GetBytes(v));
|
||||
}
|
||||
|
||||
public void WriteInt32(Int32 v)
|
||||
{
|
||||
WriteBytes(BitConverter.GetBytes(v));
|
||||
}
|
||||
public void WriteStr(string str)
|
||||
{
|
||||
WriteBytes(Encoding.UTF8.GetBytes(str));
|
||||
}
|
||||
|
||||
public void WritePadding(byte b, int len)
|
||||
{
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
WriteByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
public void PadUntil(byte b, int len)
|
||||
{
|
||||
int remain = Convert.ToInt32(len - s.Length);
|
||||
WritePadding(b, remain);
|
||||
}
|
||||
public void WriteBytes(byte[] bytes)
|
||||
{
|
||||
s.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
public void WriteByte(byte b)
|
||||
{
|
||||
s.WriteByte(b);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PopsBuilder
|
||||
{
|
||||
public class xXEccD3str0yerXx
|
||||
{
|
||||
const int SECTOR_SZ = 2352;
|
||||
const int COMPRESS_BLOCK_SZ = 0x9300;
|
||||
|
||||
public static MemoryStream RemoveEcc(string iso)
|
||||
{
|
||||
using(FileStream eccIso = File.OpenRead(iso))
|
||||
{
|
||||
MemoryStream noEccIso = new MemoryStream();
|
||||
while (eccIso.Position < eccIso.Length)
|
||||
{
|
||||
byte[] sector = new byte[SECTOR_SZ];
|
||||
|
||||
eccIso.Read(sector, 0, sector.Length);
|
||||
|
||||
// clear current sync
|
||||
Array.Fill(sector, (byte)0x00, 0x1, 0x0A);
|
||||
|
||||
// remove MSF ..
|
||||
sector[0x0C] = 0x00; // M
|
||||
sector[0x0D] = 0x00; // S
|
||||
sector[0x0E] = 0x00; // F
|
||||
|
||||
// remove ecc
|
||||
|
||||
// (only if this is not form2mode2 sector!)
|
||||
if (!(sector[0xF] == 0x2 && (sector[0x12] & 0x20) == 0x20))
|
||||
Array.Fill(sector, (byte)0x00, 0x818, 0x118);
|
||||
else if(eccIso.Position > 0x9300) // only clear if its past the system section ..
|
||||
Array.Fill(sector, (byte)0x00, 0x92C, 0x4);
|
||||
|
||||
noEccIso.Write(sector, 0, sector.Length);
|
||||
}
|
||||
|
||||
// extend ISO image to compress block sz
|
||||
int padLen = COMPRESS_BLOCK_SZ - (Convert.ToInt32(noEccIso.Length) % COMPRESS_BLOCK_SZ);
|
||||
byte[] padding = new byte[padLen];
|
||||
noEccIso.Write(padding, 0x00, padding.Length);
|
||||
|
||||
noEccIso.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return noEccIso;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,710 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class AMCTRL
|
||||
{
|
||||
static readonly Memory<byte> kirk_buf = new byte[0x0814];
|
||||
static readonly Memory<byte> kirk_buf2 = new byte[0x8014];
|
||||
|
||||
// AMCTRL keys.
|
||||
static readonly byte[] amctrl_key1 = { 0xE3, 0x50, 0xED, 0x1D, 0x91, 0x0A, 0x1F, 0xD0, 0x29, 0xBB, 0x1C, 0x3E, 0xF3, 0x40, 0x77, 0xFB };
|
||||
static readonly byte[] amctrl_key2 = { 0x13, 0x5F, 0xA4, 0x7C, 0xAB, 0x39, 0x5B, 0xA4, 0x76, 0xB8, 0xCC, 0xA9, 0x8F, 0x3A, 0x04, 0x45 };
|
||||
static readonly byte[] amctrl_key3 = { 0x67, 0x8D, 0x7F, 0xA3, 0x2A, 0x9C, 0xA0, 0xD1, 0x50, 0x8A, 0xD8, 0x38, 0x5E, 0x4B, 0x01, 0x7E };
|
||||
|
||||
public unsafe struct MAC_KEY
|
||||
{
|
||||
public int type;
|
||||
private fixed byte _key[16];
|
||||
|
||||
public Span<byte> key
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _key)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _pad[16];
|
||||
|
||||
public Span<byte> pad
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _pad)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
public int pad_size;
|
||||
}
|
||||
|
||||
public unsafe struct CIPHER_KEY
|
||||
{
|
||||
public int type;
|
||||
public int seed;
|
||||
fixed byte _key[16];
|
||||
|
||||
public Span<byte> key
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _key)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* KIRK wrapper functions.
|
||||
*/
|
||||
static int Kirk4(Span<byte> buf, int size, int type)
|
||||
{
|
||||
int retv;
|
||||
ref var hdr = ref Utils.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(buf);
|
||||
hdr.mode = KIRKEngine.KIRK_MODE_ENCRYPT_CBC;
|
||||
hdr.keyseed = type;
|
||||
hdr.data_size = size;
|
||||
|
||||
retv = KIRKEngine.sceUtilsBufferCopyWithRange(buf, size + 0x14, buf, size, KIRKEngine.KIRK_CMD_ENCRYPT_IV_0);
|
||||
|
||||
if (retv != 0)
|
||||
return -2142174447; // 0x80510311;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Kirk5(Span<byte> buf, int size)
|
||||
{
|
||||
int retv;
|
||||
ref var hdr = ref Utils.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(buf);
|
||||
hdr.mode = KIRKEngine.KIRK_MODE_ENCRYPT_CBC;
|
||||
hdr.keyseed = 0x0100;
|
||||
hdr.data_size = size;
|
||||
|
||||
retv = KIRKEngine.sceUtilsBufferCopyWithRange(buf, size + 0x14, buf, size, KIRKEngine.KIRK_CMD_ENCRYPT_IV_FUSE);
|
||||
|
||||
if (retv != 0)
|
||||
return -2142174446; // 0x80510312;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Kirk7(Span<byte> buf, int size, int type)
|
||||
{
|
||||
int retv;
|
||||
ref var hdr = ref Utils.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(buf);
|
||||
hdr.mode = KIRKEngine.KIRK_MODE_DECRYPT_CBC;
|
||||
hdr.keyseed = type;
|
||||
hdr.data_size = size;
|
||||
|
||||
retv = KIRKEngine.sceUtilsBufferCopyWithRange(buf, size + 0x14, buf, size, KIRKEngine.KIRK_CMD_DECRYPT_IV_0);
|
||||
|
||||
if (retv != 0)
|
||||
return -2142174447; // 0x80510311;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Kirk8(Span<byte> buf, int size)
|
||||
{
|
||||
int retv;
|
||||
ref var hdr = ref Utils.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(buf);
|
||||
hdr.mode = KIRKEngine.KIRK_MODE_DECRYPT_CBC;
|
||||
hdr.keyseed = 0x0100;
|
||||
hdr.data_size = size;
|
||||
|
||||
retv = KIRKEngine.sceUtilsBufferCopyWithRange(buf, size + 0x14, buf, size, KIRKEngine.KIRK_CMD_DECRYPT_IV_FUSE);
|
||||
|
||||
if (retv != 0)
|
||||
return -2142174446; // 0x80510312;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Kirk14(Span<byte> buf)
|
||||
{
|
||||
int retv;
|
||||
|
||||
retv = KIRKEngine.sceUtilsBufferCopyWithRange(buf, 0x14, null, 0, KIRKEngine.KIRK_CMD_PRNG);
|
||||
|
||||
if (retv != 0)
|
||||
return -2142174443; // 0x80510315;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encrypt_buf(Span<byte> buf, int size, Span<byte> key, int key_type)
|
||||
{
|
||||
int i, retv;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
buf[0x14 + i] ^= key[i];
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = buf)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
retv = Kirk4(buf, size, key_type);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
buf.Slice(size + 4, 16).CopyTo(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decrypt_buf(Span<byte> buf, int size, Span<byte> key, int key_type)
|
||||
{
|
||||
int i, retv;
|
||||
Span<byte> tmp = stackalloc byte[16];
|
||||
|
||||
buf.Slice(size + 0x14 - 16, 16).CopyTo(tmp);
|
||||
|
||||
retv = Kirk7(buf, size, key_type);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] ^= key[i];
|
||||
}
|
||||
|
||||
tmp.CopyTo(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cipher_buf(Span<byte> kbuf, Span<byte> dbuf, int size, ref CIPHER_KEY ckey)
|
||||
{
|
||||
int i, retv;
|
||||
Span<byte> tmp1 = stackalloc byte[16], tmp2 = stackalloc byte[16];
|
||||
|
||||
ckey.key.CopyTo(kbuf[0x14..]);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
kbuf[0x14 + i] ^= amctrl_key3[i];
|
||||
}
|
||||
|
||||
if (ckey.type == 2)
|
||||
retv = Kirk8(kbuf, 16);
|
||||
else
|
||||
retv = Kirk7(kbuf, 16, 0x39);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
kbuf[i] ^= amctrl_key2[i];
|
||||
}
|
||||
|
||||
kbuf.Slice(0, 0x10).CopyTo(tmp2);
|
||||
|
||||
if (ckey.seed == 1)
|
||||
{
|
||||
tmp1.Fill(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp2.CopyTo(tmp1);
|
||||
var tmp = ckey.seed - 1;
|
||||
MemoryMarshal.Write(tmp1[0x0c..], ref tmp);
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i += 16)
|
||||
{
|
||||
tmp2[..12].CopyTo(kbuf[(0x14 + i)..]);
|
||||
MemoryMarshal.Write(kbuf[(0x14 + i + 12)..], ref ckey.seed);
|
||||
ckey.seed += 1;
|
||||
}
|
||||
|
||||
retv = decrypt_buf(kbuf, size, tmp1, 0x63);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
dbuf[i] ^= kbuf[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int sceDrmBBMacInit(Span<byte> mkey, int type)
|
||||
{
|
||||
ref MAC_KEY macKey = ref MemoryMarshal.AsRef<MAC_KEY>(mkey);
|
||||
macKey.type = type;
|
||||
macKey.key.Clear();
|
||||
macKey.pad.Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int sceDrmBBMacUpdate(Span<byte> mkey, ReadOnlySpan<byte> buf, int size)
|
||||
{
|
||||
ref MAC_KEY macKey = ref MemoryMarshal.AsRef<MAC_KEY>(mkey);
|
||||
|
||||
int retv = 0, ksize, p, type;
|
||||
int kbuf;
|
||||
|
||||
if (macKey.pad_size > 16)
|
||||
{
|
||||
retv = -2142174462; // 0x80510302
|
||||
return retv;
|
||||
}
|
||||
|
||||
if (macKey.pad_size + size <= 16)
|
||||
{
|
||||
buf[..size].CopyTo(macKey.pad[macKey.pad_size..]);
|
||||
macKey.pad_size += size;
|
||||
retv = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
kbuf = 0x14;
|
||||
macKey.pad[..macKey.pad_size].CopyTo(kirk_buf[0x14..].Span);
|
||||
|
||||
p = macKey.pad_size;
|
||||
|
||||
macKey.pad_size += size;
|
||||
macKey.pad_size &= 0x0f;
|
||||
if (macKey.pad_size == 0)
|
||||
macKey.pad_size = 16;
|
||||
|
||||
size -= macKey.pad_size;
|
||||
buf.Slice(size, macKey.pad_size).CopyTo(macKey.pad);
|
||||
|
||||
type = (macKey.type == 2) ? 0x3A : 0x38;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
ksize = (size + p >= 0x0800) ? 0x0800 : size + p;
|
||||
buf.Slice(offset, ksize - p).CopyTo(kirk_buf[(kbuf + p)..].Span);
|
||||
retv = encrypt_buf(kirk_buf.Span, ksize, macKey.key, type);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
size -= (ksize - p);
|
||||
offset += ksize - p;
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
public static int sceDrmBBMacUpdate2(Span<byte> mkey, Span<byte> buf, int size)
|
||||
{
|
||||
ref MAC_KEY macKey = ref MemoryMarshal.AsRef<MAC_KEY>(mkey);
|
||||
|
||||
int retv = 0, ksize, p, type;
|
||||
int kbuf;
|
||||
|
||||
if (macKey.pad_size > 16)
|
||||
{
|
||||
retv = -2142174462; // 0x80510302
|
||||
return retv;
|
||||
}
|
||||
|
||||
if (macKey.pad_size + size <= 16)
|
||||
{
|
||||
buf.Slice(0, size).CopyTo(macKey.pad.Slice(macKey.pad_size));
|
||||
macKey.pad_size += size;
|
||||
retv = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
kbuf = 0x14;
|
||||
macKey.pad.Slice(0, macKey.pad_size).CopyTo(kirk_buf.Slice(0x14).Span);
|
||||
|
||||
p = macKey.pad_size;
|
||||
|
||||
macKey.pad_size += (size & 0x0f);
|
||||
//mkey.pad_size &= 0x0f;
|
||||
if (macKey.pad_size == 0)
|
||||
macKey.pad_size = 16;
|
||||
|
||||
size -= macKey.pad_size;
|
||||
buf.Slice(size, macKey.pad_size).CopyTo(macKey.pad);
|
||||
|
||||
type = (macKey.type == 2) ? 0x3A : 0x38;
|
||||
|
||||
int idx = 0;
|
||||
|
||||
ksize = size + p;
|
||||
int offset = 0;
|
||||
if (size + p >= 0x8001)
|
||||
{
|
||||
;
|
||||
buf.Slice(offset, 0x8000 - p).CopyTo(kirk_buf2.Slice(kbuf + p).Span);
|
||||
retv = encrypt_buf(kirk_buf2.Span, 0x8000, macKey.key, type);
|
||||
idx = 0x8000 - p;
|
||||
var fix = -p;
|
||||
while (retv == 0)
|
||||
{
|
||||
if (size <= 0x10000 - fix)
|
||||
{
|
||||
p = 0;
|
||||
ksize = size;
|
||||
break;
|
||||
}
|
||||
buf.Slice(offset + idx, 0x8000).CopyTo(kirk_buf2.Slice(kbuf).Span);
|
||||
retv = encrypt_buf(kirk_buf2.Span, 0x8000, macKey.key, type);
|
||||
fix = idx;
|
||||
idx += 0x8000;
|
||||
}
|
||||
|
||||
if (retv != 0)
|
||||
{
|
||||
return retv;
|
||||
}
|
||||
}
|
||||
buf.Slice(offset + idx, size - idx).CopyTo(kirk_buf2.Slice(kbuf + p).Span);
|
||||
retv = encrypt_buf(kirk_buf2.Span, ksize - idx, macKey.key, type);
|
||||
}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
public static int sceDrmBBMacFinal(Span<byte> mkey, Span<byte> buf, ReadOnlySpan<byte> vkey)
|
||||
{
|
||||
ref MAC_KEY macKey = ref MemoryMarshal.AsRef<MAC_KEY>(mkey);
|
||||
int i, retv, code;
|
||||
Span<byte> tmp = stackalloc byte[16], tmp1 = stackalloc byte[16];
|
||||
int kbuf;
|
||||
uint t0, v0, v1;
|
||||
|
||||
if (macKey.pad_size > 16)
|
||||
return -2142174462; //0x80510302;
|
||||
|
||||
code = (macKey.type == 2) ? 0x3A : 0x38;
|
||||
kbuf = 0x14;
|
||||
|
||||
kirk_buf.Slice(kbuf, 16).Span.Fill(0);
|
||||
retv = Kirk4(kirk_buf.Span, 16, code);
|
||||
if (retv != 0)
|
||||
{
|
||||
return retv;
|
||||
}
|
||||
|
||||
kirk_buf.Slice(kbuf, 16).Span.CopyTo(tmp);
|
||||
|
||||
t0 = ((tmp[0] & 0x80) > 0) ? 0x87u : 0;
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
v1 = tmp[i + 0];
|
||||
v0 = tmp[i + 1];
|
||||
v1 <<= 1;
|
||||
v0 >>= 7;
|
||||
v0 |= v1;
|
||||
tmp[i + 0] = (byte)v0;
|
||||
}
|
||||
v0 = tmp[15];
|
||||
v0 <<= 1;
|
||||
v0 ^= t0;
|
||||
tmp[15] = (byte)v0;
|
||||
|
||||
if (macKey.pad_size < 16)
|
||||
{
|
||||
t0 = ((tmp[0] & 0x80) > 0) ? 0x87u : 0;
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
v1 = tmp[i + 0];
|
||||
v0 = tmp[i + 1];
|
||||
v1 <<= 1;
|
||||
v0 >>= 7;
|
||||
v0 |= v1;
|
||||
tmp[i + 0] = (byte)v0;
|
||||
}
|
||||
v0 = tmp[15];
|
||||
v0 <<= 1;
|
||||
v0 ^= t0;
|
||||
tmp[15] = (byte)v0;
|
||||
|
||||
macKey.pad[macKey.pad_size] = 0x80;
|
||||
if (macKey.pad_size + 1 < 16)
|
||||
{
|
||||
macKey.pad.Slice(macKey.pad_size + 1, 16 - macKey.pad_size - 1).Fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
macKey.pad[i] ^= tmp[i];
|
||||
}
|
||||
|
||||
macKey.pad.CopyTo(kirk_buf.Slice(kbuf).Span);
|
||||
macKey.key.CopyTo(tmp1);
|
||||
|
||||
retv = encrypt_buf(kirk_buf.Span, 0x10, tmp1, code);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
for (i = 0; i < 0x10; i++)
|
||||
{
|
||||
tmp1[i] ^= amctrl_key1[i];
|
||||
}
|
||||
|
||||
if (macKey.type == 2)
|
||||
{
|
||||
tmp1.CopyTo(kirk_buf.Slice(kbuf).Span);
|
||||
|
||||
retv = Kirk5(kirk_buf.Span, 0x10);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
retv = Kirk4(kirk_buf.Span, 0x10, code);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
kirk_buf.Slice(kbuf, 16).Span.CopyTo(tmp1);
|
||||
}
|
||||
|
||||
if (vkey != null)
|
||||
{
|
||||
for (i = 0; i < 0x10; i++)
|
||||
{
|
||||
tmp1[i] ^= vkey[i];
|
||||
}
|
||||
tmp1.CopyTo(kirk_buf.Slice(kbuf).Span);
|
||||
|
||||
retv = Kirk4(kirk_buf.Span, 0x10, code);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
kirk_buf.Slice(kbuf, 16).Span.CopyTo(tmp1);
|
||||
}
|
||||
|
||||
tmp1.CopyTo(buf);
|
||||
|
||||
macKey.key.Fill(0);
|
||||
macKey.pad.Fill(0);
|
||||
|
||||
macKey.pad_size = 0;
|
||||
macKey.type = 0;
|
||||
retv = 0;
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static int bbmac_getkey(Span<byte> mkey, ReadOnlySpan<byte> bbmac, Span<byte> vkey)
|
||||
{
|
||||
int i, retv, type, code;
|
||||
Span<byte> tmp = stackalloc byte[16], tmp1 = stackalloc byte[16];
|
||||
int kbuf;
|
||||
ref MAC_KEY macKey = ref MemoryMarshal.AsRef<MAC_KEY>(mkey);
|
||||
|
||||
type = macKey.type;
|
||||
retv = sceDrmBBMacFinal(mkey, tmp, null);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
kbuf = 0x14;
|
||||
|
||||
if (type == 3)
|
||||
{
|
||||
bbmac[..0x10].CopyTo(kirk_buf[kbuf..].Span);
|
||||
Kirk7(kirk_buf.Span, 0x10, 0x63);
|
||||
}
|
||||
else
|
||||
{
|
||||
bbmac[..0x10].CopyTo(kirk_buf.Span);
|
||||
}
|
||||
|
||||
kirk_buf[..16].Span.CopyTo(tmp1);
|
||||
tmp1.CopyTo(kirk_buf[kbuf..].Span);
|
||||
|
||||
code = (type == 2) ? 0x3A : 0x38;
|
||||
Kirk7(kirk_buf.Span, 0x10, code);
|
||||
|
||||
for (i = 0; i < 0x10; i++)
|
||||
{
|
||||
vkey[i] = (byte)(tmp[i] ^ kirk_buf.Span[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int sceDrmBBMacFinal2(Span<byte> mkey, ReadOnlySpan<byte> hash, ReadOnlySpan<byte> vkey)
|
||||
{
|
||||
int i, retv, type;
|
||||
byte[] tmp = new byte[16];
|
||||
int kbuf;
|
||||
ref MAC_KEY macKey = ref MemoryMarshal.AsRef<MAC_KEY>(mkey);
|
||||
|
||||
type = macKey.type;
|
||||
retv = sceDrmBBMacFinal(mkey, tmp, vkey);
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
kbuf = 0x14;
|
||||
|
||||
if (type == 3)
|
||||
{
|
||||
hash[..0x10].CopyTo(kirk_buf[kbuf..].Span);
|
||||
Kirk7(kirk_buf.Span, 0x10, 0x63);
|
||||
}
|
||||
else
|
||||
{
|
||||
hash[..0x10].CopyTo(kirk_buf.Span);
|
||||
}
|
||||
|
||||
retv = 0;
|
||||
if (!kirk_buf.Span[..0x10].SequenceEqual(tmp))
|
||||
{
|
||||
retv = -2142174464; //0x80510300;
|
||||
}
|
||||
//for (i = 0; i < 0x10; i++)
|
||||
//{
|
||||
// if (kirk_buf.Span[i] != tmp[i])
|
||||
// {
|
||||
// retv = -2142174464; //0x80510300;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
BBCipher functions.
|
||||
*/
|
||||
public static int sceDrmBBCipherInit(out CIPHER_KEY ckey, int type, int mode, ReadOnlySpan<byte> header_key, ReadOnlySpan<byte> version_key, int seed)
|
||||
{
|
||||
int i, retv;
|
||||
int kbuf;
|
||||
|
||||
kbuf = 0x14;
|
||||
ckey = new CIPHER_KEY { type = type };
|
||||
if (mode == 2)
|
||||
{
|
||||
ckey.seed = seed + 1;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
ckey.key[i] = header_key[i];
|
||||
}
|
||||
if (version_key != null)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
ckey.key[i] ^= version_key[i];
|
||||
}
|
||||
}
|
||||
retv = 0;
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
ckey.seed = 1;
|
||||
retv = Kirk14(kirk_buf.Span);
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
kirk_buf.Slice(0, 0x10).CopyTo(kirk_buf.Slice(kbuf));
|
||||
kirk_buf.Slice(kbuf + 0xC, 4).Span.Fill(0);
|
||||
|
||||
if (ckey.type == 2)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
kirk_buf.Span[i + kbuf] ^= amctrl_key2[i];
|
||||
}
|
||||
retv = Kirk5(kirk_buf.Span, 0x10);
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
kirk_buf.Span[i + kbuf] ^= amctrl_key3[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
kirk_buf.Span[i + kbuf] ^= amctrl_key2[i];
|
||||
}
|
||||
retv = Kirk4(kirk_buf.Span, 0x10, 0x39);
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
kirk_buf.Span[i + kbuf] ^= amctrl_key3[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (retv != 0)
|
||||
return retv;
|
||||
|
||||
kirk_buf.Slice(kbuf, 0x10).Span.CopyTo(ckey.key);
|
||||
// kirk_buf.Slice(kbuf, 0x10).Span.CopyTo(header_key);
|
||||
|
||||
if (version_key != null)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
ckey.key[i] ^= version_key[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retv = 0;
|
||||
}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
public static int sceDrmBBCipherUpdate(ref CIPHER_KEY ckey, Span<byte> data, int size)
|
||||
{
|
||||
int p, retv, dsize;
|
||||
|
||||
retv = 0;
|
||||
p = 0;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
dsize = (size >= 0x0800) ? 0x0800 : size;
|
||||
retv = cipher_buf(kirk_buf.Span, data.Slice(p), dsize, ref ckey);
|
||||
|
||||
if (retv != 0)
|
||||
break;
|
||||
|
||||
size -= dsize;
|
||||
p += dsize;
|
||||
}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
public static int sceDrmBBCipherFinal(ref CIPHER_KEY ckey)
|
||||
{
|
||||
ckey.key.Fill(0);
|
||||
ckey.type = 0;
|
||||
ckey.seed = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public static class AesHelper
|
||||
{
|
||||
private static readonly byte[] padding = { 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
|
||||
|
||||
static readonly byte[] IV0 = new byte[16];
|
||||
static readonly byte[] Z = new byte[16];
|
||||
|
||||
public static Aes CreateAes()
|
||||
{
|
||||
var aes = Aes.Create();
|
||||
aes.KeySize = 128;
|
||||
if (aes == null)
|
||||
{
|
||||
throw new Exception("Create Aes Failed");
|
||||
}
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.IV = IV0;
|
||||
return aes;
|
||||
}
|
||||
|
||||
public static Aes CreateKirkAes()
|
||||
{
|
||||
var aes = CreateAes();
|
||||
aes.Key = KeyVault.kirk1_key;
|
||||
return aes;
|
||||
}
|
||||
|
||||
#if false
|
||||
public static byte[] Cmac(Aes aes, byte[] orgdata, int offset = 0, int len = -1)
|
||||
{
|
||||
if (len == -1)
|
||||
{
|
||||
len = orgdata.Length;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
if (offset == 0 && len == orgdata.Length)
|
||||
{
|
||||
data = orgdata;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new byte[len];
|
||||
Buffer.BlockCopy(orgdata, offset, data, 0, len);
|
||||
}
|
||||
// SubKey generation
|
||||
// step 1, AES-128 with key K is applied to an all-zero input block.
|
||||
byte[] L = AesEncrypt(aes, Z);
|
||||
|
||||
// step 2, K1 is derived through the following operation:
|
||||
byte[] FirstSubkey = Rol(L); //If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit.
|
||||
if ((L[0] & 0x80) == 0x80)
|
||||
FirstSubkey[15] ^= 0x87; // Otherwise, K1 is the exclusive-OR of c
|
||||
|
||||
// step 3, K2 is derived through the following operation:
|
||||
byte[] SecondSubkey = Rol(FirstSubkey); // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit.
|
||||
if ((FirstSubkey[0] & 0x80) == 0x80)
|
||||
SecondSubkey[15] ^= 0x87; // Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of K1 by 1 bit.
|
||||
|
||||
// MAC computing
|
||||
if (((data.Length != 0) && (data.Length % 16 == 0)))
|
||||
{
|
||||
// If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits),
|
||||
// the last block shall be exclusive-OR'ed with K1 before processing
|
||||
for (int j = 0; j < FirstSubkey.Length; j++)
|
||||
data[data.Length - 16 + j] ^= FirstSubkey[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, the last block shall be padded with 10^i
|
||||
byte[] padding = new byte[16 - data.Length % 16];
|
||||
padding[0] = 0x80;
|
||||
|
||||
data = data.Concat(padding).ToArray();
|
||||
|
||||
// and exclusive-OR'ed with K2
|
||||
for (int j = 0; j < SecondSubkey.Length; j++)
|
||||
data[data.Length - 16 + j] ^= SecondSubkey[j];
|
||||
}
|
||||
|
||||
// The result of the previous process will be the input of the last encryption.
|
||||
byte[] encResult = AesEncrypt(aes, data);
|
||||
|
||||
byte[] HashValue = new byte[16];
|
||||
//Array.Copy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length);
|
||||
Buffer.BlockCopy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length);
|
||||
|
||||
return HashValue;
|
||||
}
|
||||
|
||||
#endif
|
||||
public static void Cmac(Aes aes, Span<byte> dst, ReadOnlySpan<byte> src)
|
||||
{
|
||||
byte[] data = src.ToArray();
|
||||
// SubKey generation
|
||||
// step 1, AES-128 with key K is applied to an all-zero input block.
|
||||
byte[] L = AesEncrypt(aes, Z);
|
||||
|
||||
// step 2, K1 is derived through the following operation:
|
||||
byte[] FirstSubkey = Rol(L); //If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit.
|
||||
if ((L[0] & 0x80) == 0x80)
|
||||
FirstSubkey[15] ^= 0x87; // Otherwise, K1 is the exclusive-OR of c
|
||||
|
||||
// step 3, K2 is derived through the following operation:
|
||||
byte[] SecondSubkey = Rol(FirstSubkey); // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit.
|
||||
if ((FirstSubkey[0] & 0x80) == 0x80)
|
||||
SecondSubkey[15] ^= 0x87; // Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of K1 by 1 bit.
|
||||
|
||||
// MAC computing
|
||||
if (((data.Length != 0) && (data.Length % 16 == 0)))
|
||||
{
|
||||
// If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits),
|
||||
// the last block shall be exclusive-OR'ed with K1 before processing
|
||||
for (int j = 0; j < FirstSubkey.Length; j++)
|
||||
data[data.Length - 16 + j] ^= FirstSubkey[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, the last block shall be padded with 10^i
|
||||
byte[] padding = new byte[16 - data.Length % 16];
|
||||
padding[0] = 0x80;
|
||||
|
||||
data = data.Concat(padding).ToArray();
|
||||
|
||||
// and exclusive-OR'ed with K2
|
||||
for (int j = 0; j < SecondSubkey.Length; j++)
|
||||
data[data.Length - 16 + j] ^= SecondSubkey[j];
|
||||
}
|
||||
|
||||
// The result of the previous process will be the input of the last encryption.
|
||||
byte[] encResult = AesEncrypt(aes, data);
|
||||
|
||||
encResult.AsSpan(encResult.Length - 16,16).CopyTo(dst);
|
||||
|
||||
//byte[] HashValue = new byte[16];
|
||||
//Array.Copy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length);
|
||||
//Buffer.BlockCopy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length);
|
||||
|
||||
//return HashValue;
|
||||
}
|
||||
|
||||
private static byte[] Rol(byte[] b)
|
||||
{
|
||||
byte[] r = new byte[b.Length];
|
||||
byte carry = 0;
|
||||
|
||||
for (int i = b.Length - 1; i >= 0; i--)
|
||||
{
|
||||
ushort u = (ushort)(b[i] << 1);
|
||||
r[i] = (byte)((u & 0xff) + carry);
|
||||
carry = (byte)((u & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private static byte[] AesEncrypt(Aes aes, byte[] data)
|
||||
{
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
return encryptor.TransformFinalBlock(data, 0, data.Length);
|
||||
}
|
||||
#if false
|
||||
|
||||
public static byte[] AesEncrypt(Aes aes, byte[] data, int offset, int length)
|
||||
{
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
return encryptor.TransformFinalBlock(data, offset, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void AesEncrypt(Aes aes, Span<byte> oubput, ReadOnlySpan<byte> input, int size = 0)
|
||||
{
|
||||
byte[] buffer;
|
||||
if (size == 0)
|
||||
{
|
||||
size = input.Length;
|
||||
}
|
||||
if (size % 16 != 0)
|
||||
{
|
||||
buffer = new byte[(size + 15) / 16 * 16];
|
||||
var bufferSpan = new Span<byte>(buffer);
|
||||
input.CopyTo(bufferSpan);
|
||||
padding.AsSpan(0, buffer.Length - size).CopyTo(bufferSpan[size..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = input[..size].ToArray();
|
||||
}
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
var encData = encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
|
||||
encData.AsSpan().CopyTo(oubput);
|
||||
}
|
||||
#if false
|
||||
public static byte[] AesDecrypt(Aes aes, byte[] data, int offset, int length)
|
||||
{
|
||||
aes.Padding = PaddingMode.None;
|
||||
using var encryptor = aes.CreateDecryptor();
|
||||
int fixLength = length;
|
||||
if (length % 16 != 0)
|
||||
{
|
||||
fixLength = (length / 16 + 1) * 16;
|
||||
}
|
||||
|
||||
byte[] ret = encryptor.TransformFinalBlock(data, offset, fixLength);
|
||||
return ret.Take(length).ToArray();
|
||||
}
|
||||
|
||||
public static void AesDecrypt(Aes aes, byte[] src, byte[] dst, int size)
|
||||
{
|
||||
var tmp = AesDecrypt(aes, src, 0, size);
|
||||
Buffer.BlockCopy(tmp, 0, dst, 0, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void AesDecrypt(Aes aes, Span<byte> dst, ReadOnlySpan<byte> src, int length)
|
||||
{
|
||||
|
||||
int fixLength = length;
|
||||
if (length % 16 != 0)
|
||||
{
|
||||
fixLength = (length + 15) / 16 * 16;
|
||||
}
|
||||
var buffer = src[..fixLength].ToArray();
|
||||
using var encryptor = aes.CreateDecryptor();
|
||||
var decData = encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
|
||||
decData.AsSpan(0, length).CopyTo(dst);
|
||||
}
|
||||
|
||||
public static int AesDecrypt(ReadOnlySpan<byte> src, Span<byte> dst, ReadOnlySpan<byte> key)
|
||||
{
|
||||
Aes aes = Aes.Create();
|
||||
aes.Key = key[..16].ToArray();
|
||||
return aes.DecryptEcb(src[..16], dst, PaddingMode.None);
|
||||
}
|
||||
|
||||
public static int AesEncrypt(ReadOnlySpan<byte> src, Span<byte> dst, ReadOnlySpan<byte> key)
|
||||
{
|
||||
Aes aes = Aes.Create();
|
||||
aes.Key = key[..16].ToArray();
|
||||
return aes.EncryptEcb(src[..16], dst, PaddingMode.None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class AtracCrypto
|
||||
{
|
||||
|
||||
const int NBYTES = 0x180;
|
||||
private static uint ROTR32(uint v, int n)
|
||||
{
|
||||
n &= 32 - 1;
|
||||
return (v >> n) | (v << (32 - n));
|
||||
}
|
||||
|
||||
public static int UnscrambleAtracData(byte[] data, uint key)
|
||||
{
|
||||
int blocks = (data.Length / NBYTES) / 0x10;
|
||||
int chunks_rest = (data.Length / NBYTES) % 0x10;
|
||||
Span<uint> ptr = MemoryMarshal.Cast<byte, uint>(data);
|
||||
uint tmp2 = key;
|
||||
uint tmp;
|
||||
uint value;
|
||||
// for each block
|
||||
while (blocks > 0)
|
||||
{
|
||||
// for each chunk of block
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
{
|
||||
tmp = tmp2;
|
||||
|
||||
// for each value of chunk
|
||||
for (int k = 0; k < (NBYTES / 4); k++)
|
||||
{
|
||||
value = ptr[k];
|
||||
ptr[k] = tmp ^ value;
|
||||
tmp = tmp2 + (value * 123456789);
|
||||
}
|
||||
|
||||
tmp2 = ROTR32(tmp2, 1);
|
||||
ptr = ptr[(NBYTES / 4)..]; // pointer on next chunk
|
||||
}
|
||||
|
||||
blocks--;
|
||||
}
|
||||
|
||||
// do rest chunks
|
||||
for (int i = 0; i < chunks_rest; i++)
|
||||
{
|
||||
tmp = tmp2;
|
||||
|
||||
// for each value of chunk
|
||||
for (int k = 0; k < (NBYTES / 4); k++)
|
||||
{
|
||||
value = ptr[k];
|
||||
ptr[k] = tmp ^ value;
|
||||
tmp = tmp2 + (value * 123456789);
|
||||
}
|
||||
|
||||
tmp2 = ROTR32(tmp2, 1);
|
||||
ptr = ptr[(NBYTES / 4)..]; // next chunk
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void ScrambleAtracData(Stream input, Stream output, uint key)
|
||||
{
|
||||
int blocks = (Convert.ToInt32(input.Length) / NBYTES) / 0x10;
|
||||
int chunks_rest = (Convert.ToInt32(input.Length) / NBYTES) % 0x10;
|
||||
Span<byte> block = stackalloc byte[NBYTES];
|
||||
uint tmp2 = key;
|
||||
uint tmp;
|
||||
// for each block
|
||||
while (blocks > 0)
|
||||
{
|
||||
// for each chunk of block
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
{
|
||||
tmp = tmp2;
|
||||
|
||||
input.Read(block);
|
||||
Span<uint> ptr = MemoryMarshal.Cast<byte, uint>(block);
|
||||
|
||||
// for each value of chunk
|
||||
for (int k = 0; k < (NBYTES / 4); k++)
|
||||
{
|
||||
ptr[k] ^= tmp;
|
||||
tmp = tmp2 + (ptr[k] * 123456789);
|
||||
}
|
||||
output.Write(block);
|
||||
|
||||
tmp2 = ROTR32(tmp2, 1);
|
||||
ptr = ptr[(NBYTES / 4)..]; // pointer on next chunk
|
||||
}
|
||||
|
||||
blocks--;
|
||||
}
|
||||
|
||||
// do rest chunks
|
||||
for (int i = 0; i < chunks_rest; i++)
|
||||
{
|
||||
tmp = tmp2;
|
||||
|
||||
input.Read(block);
|
||||
Span<uint> ptr = MemoryMarshal.Cast<byte, uint>(block);
|
||||
|
||||
// for each value of chunk
|
||||
for (int k = 0; k < (NBYTES / 4); k++)
|
||||
{
|
||||
ptr[k] ^= tmp;
|
||||
tmp = tmp2 + (ptr[k] * 123456789);
|
||||
}
|
||||
output.Write(block);
|
||||
|
||||
tmp2 = ROTR32(tmp2, 1);
|
||||
ptr = ptr[(NBYTES / 4)..]; // next chunk
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
using PgdHeader = DNASStream.PgdHeader;
|
||||
using PgdDesc = DNASStream.PgdDesc;
|
||||
|
||||
public static class DNASHelper
|
||||
{
|
||||
public static int CalculateSize(int dataSize, int blockSize)
|
||||
{
|
||||
int alignSize = (dataSize + 15) & ~15;
|
||||
int tableSize = ((alignSize + blockSize - 1) & ~(blockSize - 1)) / (blockSize / 16);
|
||||
int pgdSize = 0x90 + alignSize + tableSize;
|
||||
return pgdSize;
|
||||
}
|
||||
|
||||
public static int Encrypt(Span<byte> pgdData, ReadOnlySpan<byte> data, ReadOnlySpan<byte> key, int dataSize, int keyIndex, int drmType, int flag = 2, int blockSize = 0x400)
|
||||
{
|
||||
// Additional size variables.
|
||||
var dataOffset = 0x90;
|
||||
var alignSize = (dataSize + 15) & ~15;
|
||||
var tableOffset = dataOffset + alignSize;
|
||||
var tableSize = ((alignSize + blockSize - 1) & ~(blockSize - 1)) / (blockSize / 16);
|
||||
var pgdSize = 0x90 + alignSize + tableSize;
|
||||
|
||||
if (pgdData.Length < pgdSize)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
data[..dataSize].CopyTo(pgdData[dataOffset..]);
|
||||
|
||||
ref var pgdHdr = ref Utils.AsRef<PgdHeader>(pgdData);
|
||||
pgdHdr.Magic = 0x44475000;
|
||||
pgdHdr.KeyIndex = keyIndex;
|
||||
pgdHdr.DrmType = drmType;
|
||||
|
||||
// Select the hashing, crypto and open modes.
|
||||
int macType;
|
||||
int cipherType;
|
||||
var openFlag = flag;
|
||||
if (drmType == 1)
|
||||
{
|
||||
macType = 1;
|
||||
cipherType = 1;
|
||||
openFlag |= 4;
|
||||
if (keyIndex > 1)
|
||||
{
|
||||
macType = 3;
|
||||
openFlag |= 0xc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
macType = 2;
|
||||
cipherType = 2;
|
||||
}
|
||||
|
||||
// Select the fixed DNAS key.
|
||||
|
||||
|
||||
byte[] dnasKey = null;
|
||||
|
||||
if ((openFlag & 2) != 0)
|
||||
{
|
||||
dnasKey = DNASStream.DnasKey1;
|
||||
}
|
||||
else if ((openFlag & 1) != 0)
|
||||
{
|
||||
dnasKey = DNASStream.DnasKey2;
|
||||
}
|
||||
|
||||
if (dnasKey == null)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
// Set the decryption parameters in the decrypted header.
|
||||
ref var pgdDesc = ref Utils.AsRef<PgdDesc>(pgdHdr.PgdDesc);
|
||||
pgdDesc.DataSize = dataSize;
|
||||
pgdDesc.BlockSize = blockSize;
|
||||
pgdDesc.DataOffset = dataOffset;
|
||||
|
||||
// Generate random header and data keys.
|
||||
RandomNumberGenerator.Fill(pgdData.Slice(0x10, 0x30));
|
||||
|
||||
// Encrypt the data.
|
||||
DNASStream.DoBBCipher(pgdData[dataOffset..], alignSize, 0, key, pgdDesc.Key, cipherType);
|
||||
|
||||
// Build data MAC hash.
|
||||
var tableNum = tableSize / 16;
|
||||
for (int i = 0; i < tableNum; i++)
|
||||
{
|
||||
int rsize = alignSize - i * blockSize;
|
||||
if (rsize > blockSize)
|
||||
rsize = blockSize;
|
||||
if (keyIndex < 3)
|
||||
{
|
||||
BuildBBMac(pgdData[(dataOffset + i * blockSize)..], rsize, key,
|
||||
pgdData[(tableOffset + i * 16)..],
|
||||
macType);
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildBBMac(pgdData[(dataOffset + i * blockSize)..], rsize, key,
|
||||
pgdData[(tableOffset + i * 16)..],
|
||||
macType);
|
||||
}
|
||||
}
|
||||
|
||||
// Build table MAC hash.
|
||||
BuildBBMac(pgdData.Slice(tableOffset), tableSize, key, pgdHdr.MacTableHash, macType);
|
||||
|
||||
// Encrypt the PGD header block (0x30 bytes).
|
||||
DNASStream.DoBBCipher(pgdHdr.PgdDesc, 0x30, 0, key, pgdHdr.DescKey, cipherType);
|
||||
|
||||
// Build MAC hash at 0x70 (key hash).
|
||||
BuildBBMac(pgdData, 0x70, key, pgdHdr.Hash70, macType);
|
||||
|
||||
// Build MAC hash at 0x80 (DNAS hash).
|
||||
BuildBBMac(pgdData, 0x80, dnasKey, pgdHdr.Hash80, macType);
|
||||
|
||||
return pgdSize;
|
||||
}
|
||||
|
||||
static int BuildBBMac(ReadOnlySpan<byte> data, int size, ReadOnlySpan<byte> key, Span<byte> hash, int macType, int seed = 0)
|
||||
{
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<AMCTRL.MAC_KEY>()];
|
||||
Span<byte> tmpKey = stackalloc byte[0x10];
|
||||
int ret = unchecked((int)0x80510201);
|
||||
if (hash != null)
|
||||
{
|
||||
ret = AMCTRL.sceDrmBBMacInit(mkey, macType);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBMacUpdate(mkey, data, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
key.CopyTo(tmpKey);
|
||||
if (seed != 0)
|
||||
{
|
||||
var tmpXor = MemoryMarshal.Cast<byte, int>(tmpKey);
|
||||
tmpXor[0] ^= seed;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBMacFinal(mkey, hash, tmpKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = unchecked((int)0x80510207);
|
||||
}
|
||||
|
||||
if (macType == 3)
|
||||
{
|
||||
Utils.BuildDrmBBMacFinal2(hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,636 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class DNASStream : Stream
|
||||
{
|
||||
internal static readonly byte[] DnasKeyBase =
|
||||
{
|
||||
0x2A, 0x05, 0x54, 0x40, 0x62, 0xD9, 0x1F, 0xE3, 0xF2, 0xD0, 0x2B, 0xC6, 0x21, 0xFF, 0x20, 0x0E,
|
||||
0xB1, 0x44, 0x28, 0xDF, 0x0A, 0xCD, 0x14, 0x5B, 0xC8, 0x19, 0x36, 0x90, 0xD1, 0x42, 0x99, 0x2F
|
||||
};
|
||||
|
||||
internal static readonly byte[] DnasKey1 =
|
||||
{
|
||||
0xED, 0xE2, 0x5D, 0x2D, 0xBB, 0xF8, 0x12, 0xE5, 0x3C, 0x5C, 0x59, 0x32, 0xFA, 0xE3, 0xE2, 0x43
|
||||
};
|
||||
|
||||
internal static readonly byte[] DnasKey2 =
|
||||
{
|
||||
0x27, 0x74, 0xFB, 0xEB, 0xA4, 0xA0, 0x01, 0xD7, 0x02, 0x56, 0x9E, 0x33, 0x8C, 0x19, 0x57, 0x83
|
||||
};
|
||||
|
||||
private static Memory<byte> _gMemory = new byte[0x640];
|
||||
private static Memory<byte> _gCipherMemory = new byte[0x200];
|
||||
|
||||
private Stream _baseStream;
|
||||
private byte[] _versionKey;
|
||||
|
||||
private long _position;
|
||||
private long _pgdOffset;
|
||||
private int _keyIndex;
|
||||
private int _openFlag;
|
||||
private long _dataOffset;
|
||||
private long _tableOffset;
|
||||
|
||||
public int KeyIndex => _keyIndex;
|
||||
|
||||
private PgdDesc _desc;
|
||||
|
||||
internal unsafe struct PgdHeader
|
||||
{
|
||||
public uint Magic;
|
||||
public int KeyIndex;
|
||||
public int DrmType;
|
||||
public int Unk12;
|
||||
private fixed byte _descKey[0x10];
|
||||
public Span<byte> DescKey
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _descKey)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _keyHash[0x10];
|
||||
public Span<byte> KeyHash
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _keyHash)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _pgdDesc[0x30];
|
||||
public Span<byte> PgdDesc
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _pgdDesc)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x30);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _macTableHash[0x10];
|
||||
public Span<byte> MacTableHash
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _macTableHash)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _hash70[0x10];
|
||||
public Span<byte> Hash70
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _hash70)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _hash80[0x10];
|
||||
public Span<byte> Hash80
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _hash80)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe struct PgdDesc
|
||||
{
|
||||
private fixed byte _key[0x10];
|
||||
|
||||
public Span<byte> Key
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _key)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
public int Version;
|
||||
public int DataSize;
|
||||
public int BlockSize;
|
||||
public int DataOffset;
|
||||
private fixed byte _unk20[0x10];
|
||||
|
||||
public Span<byte> Unk20
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _unk20)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int BlockSize => _desc.BlockSize;
|
||||
|
||||
public DNASStream(Stream stream, long pgdOffset, ReadOnlySpan<byte> versionKey, int flag = 2)
|
||||
: this(stream, pgdOffset, versionKey.ToArray(), flag)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DNASStream(Stream stream, long pgdOffset, byte[] versionKey = null, int flag = 2)
|
||||
{
|
||||
_baseStream = stream;
|
||||
_pgdOffset = pgdOffset;
|
||||
var offset = stream.Seek(pgdOffset, SeekOrigin.Begin);
|
||||
if (offset != _pgdOffset)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
Span<byte> hdr = _gMemory[..0x90].Span;
|
||||
var size = stream.Read(hdr);
|
||||
if (size != 0x90)
|
||||
{
|
||||
throw new ArgumentException("stream too small", nameof(stream));
|
||||
}
|
||||
var header = Utils.AsRef<PgdHeader>(hdr);
|
||||
_keyIndex = header.KeyIndex;
|
||||
if (_keyIndex == 1)
|
||||
{
|
||||
_versionKey = versionKey ?? new byte[16];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (versionKey == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(versionKey));
|
||||
}
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<AMCTRL.MAC_KEY>()];
|
||||
AMCTRL.sceDrmBBMacInit(mkey, 1);
|
||||
AMCTRL.sceDrmBBMacUpdate(mkey, DnasKeyBase, (_keyIndex - 1) * 0x10);
|
||||
AMCTRL.sceDrmBBMacFinal(mkey, _versionKey, versionKey);
|
||||
return;
|
||||
}
|
||||
|
||||
int macType;
|
||||
int cipherType;
|
||||
|
||||
if (header.DrmType == 1)
|
||||
{
|
||||
flag |= 4;
|
||||
macType = 1;
|
||||
cipherType = 1;
|
||||
if (header.KeyIndex > 1)
|
||||
{
|
||||
flag |= 0xc;
|
||||
macType = 3;
|
||||
}
|
||||
}
|
||||
else if (header.DrmType == 0 && (flag & 4) == 0)
|
||||
{
|
||||
macType = 2;
|
||||
cipherType = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
byte[] dnasKey = null;
|
||||
|
||||
if ((flag & 2) != 0)
|
||||
{
|
||||
dnasKey = DnasKey1;
|
||||
}
|
||||
else if ((flag & 1) != 0)
|
||||
{
|
||||
dnasKey = DnasKey2;
|
||||
}
|
||||
|
||||
if (dnasKey == null)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
var ret = CheckBBMac(hdr, 0x80, dnasKey, header.Hash80, macType);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new IOException("Wrong MAC 0x80");
|
||||
}
|
||||
|
||||
if (!Utils.isEmpty(_versionKey, 0x10))
|
||||
{
|
||||
ret = CheckBBMac(hdr, 0x70, _versionKey, header.Hash70, macType);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = GetMacKey(hdr, 0x70, _versionKey, header.Hash70, macType);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new IOException("Wrong MAC 0x70");
|
||||
}
|
||||
|
||||
ret = DoBBCipher(header.PgdDesc, 0x30, 0, _versionKey, header.DescKey, cipherType);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new IOException($"Error 0x{ret:X8}");
|
||||
}
|
||||
var desc = Utils.AsRef<PgdDesc>(header.PgdDesc);
|
||||
if (desc.Version != 0)
|
||||
{
|
||||
throw new IOException($"Error 0x{8051020:X8}");
|
||||
}
|
||||
|
||||
if (desc.BlockSize != 0x400)
|
||||
{
|
||||
throw new IOException($"Error 0x{80510204:X8}");
|
||||
}
|
||||
|
||||
_openFlag = flag | 0x10;
|
||||
_desc = desc;
|
||||
_dataOffset = _desc.DataOffset + pgdOffset;
|
||||
var blockSize = desc.BlockSize;
|
||||
var alignSize = (desc.DataSize + 15) & ~15;
|
||||
var tableSize = ((alignSize + blockSize - 1) & ~(blockSize - 1)) / (blockSize / 16);
|
||||
_tableOffset = pgdOffset + 0x90 + alignSize;
|
||||
if (header.KeyIndex < 3 && 0x7ffff < tableSize)
|
||||
{
|
||||
|
||||
}
|
||||
{
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<AMCTRL.MAC_KEY>()];
|
||||
ret = AMCTRL.sceDrmBBMacInit(mkey, macType);
|
||||
stream.Seek(_tableOffset, SeekOrigin.Begin);
|
||||
int read = 0;
|
||||
if (tableSize != 0)
|
||||
{
|
||||
Span<byte> dataBuf = new byte[0x400];
|
||||
do
|
||||
{
|
||||
var tmpSize = tableSize - read;
|
||||
if (tmpSize > 0x400)
|
||||
{
|
||||
tmpSize = 0x400;
|
||||
}
|
||||
|
||||
var data = dataBuf.Slice(0, tmpSize);
|
||||
var readSize = stream.Read(data);
|
||||
if (readSize != tmpSize)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
ret = AMCTRL.sceDrmBBMacUpdate(mkey, data, tmpSize);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
read += 0x400;
|
||||
} while (read < tableSize);
|
||||
}
|
||||
ret = AMCTRL.sceDrmBBMacFinal2(mkey, header.MacTableHash, _versionKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new IOException($"Error 0x{80510204:X8}");
|
||||
}
|
||||
}
|
||||
|
||||
_position = 0;
|
||||
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int ret;
|
||||
Span<byte> bufferSpan = buffer;
|
||||
if ((_openFlag & 0x10) == 0)
|
||||
{
|
||||
throw new IOException($"Error 0x{80510207:X8}");
|
||||
}
|
||||
|
||||
var dataSize = _desc.DataSize;
|
||||
var seekOffset = _position;
|
||||
if (seekOffset < dataSize)
|
||||
{
|
||||
var macType = 2;
|
||||
var cipherType = 2;
|
||||
if ((_openFlag & 4) != 0)
|
||||
{
|
||||
macType = 1;
|
||||
if ((_openFlag & 8) != 0)
|
||||
{
|
||||
macType = 3;
|
||||
}
|
||||
|
||||
cipherType = 1;
|
||||
}
|
||||
|
||||
var endOffset = dataSize;
|
||||
if (seekOffset + count <= dataSize)
|
||||
{
|
||||
endOffset = (int)(seekOffset + count);
|
||||
}
|
||||
|
||||
var blockSize = _desc.BlockSize;
|
||||
var alignOffset = (int)seekOffset;
|
||||
var totalReadSize = 0;
|
||||
while (true)
|
||||
{
|
||||
if (endOffset <= alignOffset)
|
||||
{
|
||||
return totalReadSize;
|
||||
}
|
||||
|
||||
if ((_openFlag & 0x10) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var align = alignOffset & blockSize - 1;
|
||||
|
||||
alignOffset -= align;
|
||||
var alignBlockSize = endOffset - alignOffset;
|
||||
int readBytes;
|
||||
int uVar6;
|
||||
Span<byte> readBuffer;
|
||||
if (align == 0 && blockSize <= alignBlockSize)
|
||||
{
|
||||
readBytes = alignBlockSize & ~(blockSize - 1);
|
||||
readBuffer = bufferSpan;
|
||||
uVar6 = readBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
readBytes = blockSize;
|
||||
readBuffer = _gMemory.Span;
|
||||
if (dataSize < alignOffset + blockSize)
|
||||
{
|
||||
readBytes = (dataSize - alignOffset + 15) & ~15;
|
||||
}
|
||||
|
||||
uVar6 = blockSize;
|
||||
if (endOffset < alignOffset + blockSize)
|
||||
{
|
||||
uVar6 = alignBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
_baseStream.Seek(_dataOffset + alignOffset, SeekOrigin.Begin);
|
||||
var realReadBytes = _baseStream.Read(readBuffer.Slice(0, readBytes));
|
||||
if (realReadBytes < readBytes)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
var tableOffset = (alignOffset / blockSize) * 0x10;
|
||||
_baseStream.Seek(_tableOffset + tableOffset, SeekOrigin.Begin);
|
||||
var blockNr = 0;
|
||||
if (readBytes != 0)
|
||||
{
|
||||
var tableReadOffset = 0;
|
||||
var cipherSpan = _gCipherMemory.Span;
|
||||
do
|
||||
{
|
||||
alignBlockSize = readBytes - tableReadOffset;
|
||||
if (blockSize < readBytes - tableReadOffset)
|
||||
{
|
||||
alignBlockSize = blockSize;
|
||||
}
|
||||
|
||||
if ((blockNr & 0x1f) == 0)
|
||||
{
|
||||
if (blockSize == 0)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
var tableBlock = readBytes / blockSize - blockNr;
|
||||
if (tableBlock == 0)
|
||||
{
|
||||
tableBlock = 1;
|
||||
}
|
||||
|
||||
if (0x20 < tableBlock)
|
||||
{
|
||||
tableBlock = 0x20;
|
||||
}
|
||||
cipherSpan.Fill(0);
|
||||
realReadBytes = _baseStream.Read(cipherSpan[..(tableBlock * 16)]);
|
||||
if (realReadBytes < tableBlock * 16)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
if (_keyIndex < 3)
|
||||
{
|
||||
ret = CheckBBMac(readBuffer[tableReadOffset..], alignBlockSize, _versionKey,
|
||||
cipherSpan.Slice((blockNr & 0x1f) * 16), macType);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = CheckBBMac(readBuffer[tableReadOffset..], alignBlockSize, _versionKey,
|
||||
cipherSpan[((blockNr & 0x1f) * 16)..], macType, alignOffset);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
tableReadOffset += blockSize;
|
||||
blockNr++;
|
||||
} while (tableReadOffset < readBytes);
|
||||
}
|
||||
ret = DoBBCipher(readBuffer, readBytes, alignOffset + align >> 4, _versionKey, _desc.Key, cipherType);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
var iVar2 = uVar6 - align;
|
||||
seekOffset += iVar2;
|
||||
_position = seekOffset;
|
||||
if (readBuffer != bufferSpan)
|
||||
{
|
||||
readBuffer.Slice(align, iVar2).CopyTo(bufferSpan);
|
||||
}
|
||||
bufferSpan = bufferSpan[iVar2..];
|
||||
totalReadSize += iVar2;
|
||||
alignOffset += uVar6;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
if ((_openFlag & 0x10) == 0)
|
||||
{
|
||||
throw new IOException($"Error 0x{80510206:X8}");
|
||||
}
|
||||
|
||||
var dataSize = _desc.DataSize;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
break;
|
||||
case SeekOrigin.Current:
|
||||
offset += -_position;
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
offset += dataSize;
|
||||
break;
|
||||
}
|
||||
if (offset > 0xffffffff)
|
||||
{
|
||||
offset = 0xffffffff;
|
||||
}
|
||||
|
||||
if (offset > dataSize)
|
||||
{
|
||||
offset = dataSize;
|
||||
}
|
||||
|
||||
_position = offset;
|
||||
|
||||
return _position;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
internal static int CheckBBMac(ReadOnlySpan<byte> data, int size, ReadOnlySpan<byte> key, ReadOnlySpan<byte> hash, int macType, int seed = 0)
|
||||
{
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<AMCTRL.MAC_KEY>()];
|
||||
Span<byte> tmpKey = stackalloc byte[0x10];
|
||||
int ret = unchecked((int)0x80510201);
|
||||
if (hash != null)
|
||||
{
|
||||
ret = AMCTRL.sceDrmBBMacInit(mkey, macType);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBMacUpdate(mkey, data, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
key.CopyTo(tmpKey);
|
||||
if (seed != 0)
|
||||
{
|
||||
var tmpXor = MemoryMarshal.Cast<byte, int>(tmpKey);
|
||||
tmpXor[0] ^= seed;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBMacFinal2(mkey, hash, tmpKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = unchecked((int)0x80510207);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static int GetMacKey(ReadOnlySpan<byte> data, int size, Span<byte> key, ReadOnlySpan<byte> hash, int macType, int seed = 0)
|
||||
{
|
||||
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<AMCTRL.MAC_KEY>()];
|
||||
Span<byte> tmpKey = stackalloc byte[0x10];
|
||||
int ret = unchecked((int)0x80510201);
|
||||
if (hash != null)
|
||||
{
|
||||
ret = AMCTRL.sceDrmBBMacInit(mkey, macType);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBMacUpdate(mkey, data, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AMCTRL.bbmac_getkey(mkey, hash, tmpKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = unchecked((int)0x80510207);
|
||||
}
|
||||
if (seed != 0)
|
||||
{
|
||||
var tmpXor = MemoryMarshal.Cast<byte, int>(tmpKey);
|
||||
tmpXor[0] ^= seed;
|
||||
}
|
||||
tmpKey.CopyTo(key);
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static int DoBBCipher(Span<byte> data, int size, int seed, ReadOnlySpan<byte> versionKey,
|
||||
ReadOnlySpan<byte> headerKey, int cipherType)
|
||||
{
|
||||
int ret = AMCTRL.sceDrmBBCipherInit(out var ckey, cipherType, 2, headerKey, versionKey, seed);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBCipherUpdate(ref ckey, data, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AMCTRL.sceDrmBBCipherFinal(ref ckey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public byte[] VersionKey => _versionKey;
|
||||
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
public override bool CanSeek => _baseStream.CanSeek;
|
||||
public override bool CanWrite => false;
|
||||
public override long Length => _desc.DataSize;
|
||||
public override long Position
|
||||
{
|
||||
get => _position;
|
||||
set => _position = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using PspCrypto.Security.Cryptography;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public static class ECDsaHelper
|
||||
{
|
||||
public static ECCurve SetCurve(byte[] p, byte[] a, byte[] b, byte[] n, byte[] gx, byte[] gy)
|
||||
{
|
||||
return new ECCurve
|
||||
{
|
||||
A = a,
|
||||
B = b,
|
||||
Prime = p,
|
||||
Order = n,
|
||||
CurveType = ECCurve.ECCurveType.PrimeShortWeierstrass,
|
||||
Cofactor = new byte[] { 0x01 },
|
||||
G = new ECPoint { X = gx, Y = gy }
|
||||
};
|
||||
}
|
||||
|
||||
public static (byte[], ECPoint) GenerateKey(byte[] p, byte[] a, byte[] b, byte[] n, byte[] gx, byte[] gy)
|
||||
{
|
||||
var curve = new ECCurve
|
||||
{
|
||||
A = a,
|
||||
B = b,
|
||||
Prime = p,
|
||||
Order = n,
|
||||
CurveType = ECCurve.ECCurveType.PrimeShortWeierstrass,
|
||||
Cofactor = new byte[] { 0x01 },
|
||||
G = { X = gx, Y = gy }
|
||||
};
|
||||
var ecdsa = new ECDsaManaged();
|
||||
ecdsa.GenerateKey(curve);
|
||||
var parameter = ecdsa.ExportExplicitParameters(true);
|
||||
return (parameter.D, parameter.Q);
|
||||
}
|
||||
|
||||
public static ECDsa Create(ECCurve curve, byte[] privateKey)
|
||||
{
|
||||
return Create(curve, privateKey, new byte[privateKey.Length], new byte[privateKey.Length]);
|
||||
}
|
||||
|
||||
|
||||
public static ECDsa Create(ECCurve curve, byte[] pubx, byte[] puby)
|
||||
{
|
||||
return Create(curve, null, pubx, puby);
|
||||
}
|
||||
|
||||
public static ECDsa Create(ECCurve curve, Span<byte> pubx, Span<byte> puby)
|
||||
{
|
||||
return Create(curve, null, pubx.ToArray(), puby.ToArray());
|
||||
}
|
||||
|
||||
public static ECDsa Create(ECCurve curve, byte[] privateKey, byte[] pubx, byte[] puby, bool ebootPbp = false, int type = 1)
|
||||
{
|
||||
var par = new ECParameters
|
||||
{
|
||||
Curve = curve,
|
||||
D = privateKey,
|
||||
Q = { X = pubx, Y = puby }
|
||||
};
|
||||
return new ECDsaManaged(par, ebootPbp, type);
|
||||
}
|
||||
|
||||
public static ECDsa CreateNet(ECCurve curve, byte[] privateKey, byte[] pubx, byte[] puby)
|
||||
{
|
||||
var par = new ECParameters
|
||||
{
|
||||
Curve = curve,
|
||||
D = privateKey,
|
||||
Q = { X = pubx, Y = puby }
|
||||
};
|
||||
return ECDsa.Create(par);
|
||||
}
|
||||
|
||||
public static void SignNpImageHeader(Span<byte> npHdr)
|
||||
{
|
||||
var curve = SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2,
|
||||
KeyVault.Gy2);
|
||||
using var ecdsa = Create(curve, KeyVault.ec_Priv2, KeyVault.Px2, KeyVault.Py2);
|
||||
var hash = ecdsa.SignData(npHdr[..0xD8].ToArray(), HashAlgorithmName.SHA1);
|
||||
hash.CopyTo(npHdr[0xD8..]);
|
||||
}
|
||||
|
||||
public static bool VerifyEdat(Span<byte> edat)
|
||||
{
|
||||
var curve = SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2,
|
||||
KeyVault.Gy2);
|
||||
using var ecdsa = Create(curve, KeyVault.EdatPx, KeyVault.EdatPy);
|
||||
return ecdsa.VerifyData(edat[..0x58], edat.Slice(0x58, 0x28), HashAlgorithmName.SHA1);
|
||||
}
|
||||
|
||||
public static void SignEdat(Span<byte> edat)
|
||||
{
|
||||
var curve = SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2,
|
||||
KeyVault.Gy2);
|
||||
using var ecdsa = Create(curve, KeyVault.EdatPirv, KeyVault.EdatPx, KeyVault.EdatPy);
|
||||
var sig = ecdsa.SignData(edat[..0x58].ToArray(), HashAlgorithmName.SHA1);
|
||||
sig.CopyTo(edat[0x58..]);
|
||||
}
|
||||
|
||||
public static void SignParamSfo(ReadOnlySpan<byte> param, Span<byte> sig)
|
||||
{
|
||||
var curve = SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2,
|
||||
KeyVault.Gy2);
|
||||
using var ecdsa = Create(curve, KeyVault.ec_Priv2, KeyVault.Px2, KeyVault.Py2);
|
||||
var sigTmp = ecdsa.SignData(param.ToArray(), HashAlgorithmName.SHA1);
|
||||
sigTmp.CopyTo(sig);
|
||||
}
|
||||
|
||||
public static bool VerifyEbootPbp(Span<byte> data, Span<byte> sig)
|
||||
{
|
||||
var sha224 = SHA224.Create();
|
||||
var hash = sha224.ComputeHash(data.ToArray());
|
||||
var curve = SetCurve(KeyVault.Eboot_p, KeyVault.Eboot_a, KeyVault.Eboot_b, KeyVault.Eboot_N, KeyVault.Eboot_Gx,
|
||||
KeyVault.Eboot_Gy);
|
||||
using var ecdsa = Create(curve, KeyVault.Eboot_priv2, KeyVault.Eboot_pub2x, KeyVault.Eboot_pub2y, true);
|
||||
return ecdsa.VerifyHash(hash, sig);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using ECDsaTest.SafeHandles;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class Crypto
|
||||
{
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_BigNumDestroy")]
|
||||
internal static extern void BigNumDestroy(IntPtr a);
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_BigNumToBinary")]
|
||||
private static extern unsafe int BigNumToBinary(SafeBignumHandle a, byte* to);
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetBigNumBytes")]
|
||||
private static extern int GetBigNumBytes(SafeBignumHandle a);
|
||||
|
||||
|
||||
internal static byte[] ExtractBignum(IntPtr bignum, int targetSize)
|
||||
{
|
||||
// Given that the only reference held to bignum is an IntPtr, create an unowned SafeHandle
|
||||
// to ensure that we don't destroy the key after extraction.
|
||||
using (SafeBignumHandle handle = new SafeBignumHandle(bignum, ownsHandle: false))
|
||||
{
|
||||
return ExtractBignum(handle, targetSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe byte[] ExtractBignum(SafeBignumHandle bignum, int targetSize)
|
||||
{
|
||||
if (bignum == null || bignum.IsInvalid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int compactSize = GetBigNumBytes(bignum);
|
||||
|
||||
if (targetSize < compactSize)
|
||||
{
|
||||
targetSize = compactSize;
|
||||
}
|
||||
|
||||
// OpenSSL BIGNUM values do not record leading zeroes.
|
||||
// Windows Crypt32 does.
|
||||
//
|
||||
// Since RSACryptoServiceProvider already checks that RSAParameters.DP.Length is
|
||||
// exactly half of RSAParameters.Modulus.Length, we need to left-pad (big-endian)
|
||||
// the array with zeroes.
|
||||
int offset = targetSize - compactSize;
|
||||
|
||||
byte[] buf = new byte[targetSize];
|
||||
|
||||
fixed (byte* to = buf)
|
||||
{
|
||||
byte* start = to + offset;
|
||||
BigNumToBinary(bignum, start);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class Crypto
|
||||
{
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrClearError")]
|
||||
internal static extern ulong ErrClearError();
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrGetErrorAlloc")]
|
||||
private static extern ulong ErrGetErrorAlloc([MarshalAs(UnmanagedType.Bool)] out bool isAllocFailure);
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ErrErrorStringN")]
|
||||
private static extern unsafe void ErrErrorStringN(ulong e, byte* buf, int len);
|
||||
|
||||
private static unsafe string ErrErrorStringN(ulong error)
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
fixed (byte* buf = &buffer[0])
|
||||
{
|
||||
ErrErrorStringN(error, buf, buffer.Length);
|
||||
return Marshal.PtrToStringAnsi((IntPtr)buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static Exception CreateOpenSslCryptographicException()
|
||||
{
|
||||
// The Windows cryptography library reports error codes through
|
||||
// Marshal.GetLastWin32Error, which has a single value when the
|
||||
// function exits, last writer wins.
|
||||
//
|
||||
// OpenSSL maintains an error queue. Calls to ERR_get_error read
|
||||
// values out of the queue in the order that ERR_set_error wrote
|
||||
// them. Nothing enforces that a single call into an OpenSSL
|
||||
// function will guarantee at-most one error being set.
|
||||
//
|
||||
// In order to maintain parity in how error flows look between the
|
||||
// Windows code and the OpenSSL-calling code, drain the queue
|
||||
// whenever an Exception is desired, and report the exception
|
||||
// related to the last value in the queue.
|
||||
bool isAllocFailure;
|
||||
ulong error = ErrGetErrorAlloc(out isAllocFailure);
|
||||
ulong lastRead = error;
|
||||
bool lastIsAllocFailure = isAllocFailure;
|
||||
|
||||
// 0 (there's no named constant) is only returned when the calls
|
||||
// to ERR_get_error exceed the calls to ERR_set_error.
|
||||
while (lastRead != 0)
|
||||
{
|
||||
error = lastRead;
|
||||
isAllocFailure = lastIsAllocFailure;
|
||||
|
||||
lastRead = ErrGetErrorAlloc(out lastIsAllocFailure);
|
||||
}
|
||||
|
||||
// If we're in an error flow which results in an Exception, but
|
||||
// no calls to ERR_set_error were made, throw the unadorned
|
||||
// CryptographicException.
|
||||
if (error == 0)
|
||||
{
|
||||
return new CryptographicException();
|
||||
}
|
||||
|
||||
if (isAllocFailure)
|
||||
{
|
||||
return new OutOfMemoryException();
|
||||
}
|
||||
|
||||
// Even though ErrGetError returns ulong (C++ unsigned long), we
|
||||
// really only expect error codes in the UInt32 range
|
||||
Debug.Assert(error <= uint.MaxValue, "ErrGetError should only return error codes in the UInt32 range.");
|
||||
|
||||
// If there was an error code, and it wasn't something handled specially,
|
||||
// use the OpenSSL error string as the message to a CryptographicException.
|
||||
return new OpenSslCryptographicException(unchecked((int)error), ErrErrorStringN(error));
|
||||
}
|
||||
|
||||
private sealed class OpenSslCryptographicException : CryptographicException
|
||||
{
|
||||
internal OpenSslCryptographicException(int errorCode, string message)
|
||||
: base(message)
|
||||
{
|
||||
HResult = errorCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using ECDsaTest.SafeHandles;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class Crypto
|
||||
{
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByExplicitParameters")]
|
||||
internal static extern SafeEcKeyHandle EcKeyCreateByExplicitParameters(
|
||||
ECCurve.ECCurveType curveType,
|
||||
byte[] qx, int qxLength,
|
||||
byte[] qy, int qyLength,
|
||||
byte[] d, int dLength,
|
||||
byte[] p, int pLength,
|
||||
byte[] a, int aLength,
|
||||
byte[] b, int bLength,
|
||||
byte[] gx, int gxLength,
|
||||
byte[] gy, int gyLength,
|
||||
byte[] order, int nLength,
|
||||
byte[] cofactor, int cofactorLength,
|
||||
byte[] seed, int seedLength);
|
||||
|
||||
internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve)
|
||||
{
|
||||
byte[] p;
|
||||
if (curve.IsPrime)
|
||||
{
|
||||
p = curve.Prime;
|
||||
}
|
||||
else if (curve.IsCharacteristic2)
|
||||
{
|
||||
p = curve.Polynomial;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException(string.Format("The specified curve '{0}' or its parameters are not valid for this platform.", curve.CurveType.ToString()));
|
||||
}
|
||||
|
||||
SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters(
|
||||
curve.CurveType,
|
||||
null, 0,
|
||||
null, 0,
|
||||
null, 0,
|
||||
p, p.Length,
|
||||
curve.A, curve.A.Length,
|
||||
curve.B, curve.B.Length,
|
||||
curve.G.X, curve.G.X.Length,
|
||||
curve.G.Y, curve.G.Y.Length,
|
||||
curve.Order, curve.Order.Length,
|
||||
curve.Cofactor, curve.Cofactor.Length,
|
||||
curve.Seed, curve.Seed == null ? 0 : curve.Seed.Length);
|
||||
|
||||
if (key == null || key.IsInvalid)
|
||||
{
|
||||
if (key != null)
|
||||
key.Dispose();
|
||||
throw Interop.Crypto.CreateOpenSslCryptographicException();
|
||||
}
|
||||
|
||||
// EcKeyCreateByExplicitParameters may have polluted the error queue, but key was good in the end.
|
||||
// Clean up the error queue.
|
||||
Interop.Crypto.ErrClearError();
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using ECDsaTest;
|
||||
using ECDsaTest.SafeHandles;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class Crypto
|
||||
{
|
||||
internal static bool EcDsaSignEx(ReadOnlySpan<byte> dgst, Span<byte> sig, [In, Out] ref int siglen,
|
||||
ReadOnlySpan<byte> kinv, ReadOnlySpan<byte> rp, SafeEcKeyHandle ecKey) => EcDsaSignEx(
|
||||
ref MemoryMarshal.GetReference(dgst), dgst.Length, ref MemoryMarshal.GetReference(sig), ref siglen,
|
||||
ref MemoryMarshal.GetReference(kinv), kinv.Length, ref MemoryMarshal.GetReference(rp), rp.Length, ecKey);
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcDsaSignEx")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool EcDsaSignEx(ref byte dgst, int dlen, ref byte sig, [In, Out] ref int siglen,
|
||||
ref byte kinv, int kinvlen, ref byte rp, int rplen, SafeEcKeyHandle ecKey);
|
||||
|
||||
// returns the maximum length of a DER encoded ECDSA signature created with this key.
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcDsaSize")]
|
||||
private static extern int CryptoNative_EcDsaSize(SafeEcKeyHandle ecKey);
|
||||
|
||||
internal static int EcDsaSize(SafeEcKeyHandle ecKey)
|
||||
{
|
||||
int ret = CryptoNative_EcDsaSize(ecKey);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
throw CreateOpenSslCryptographicException();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
internal static PspParameter EcPspParameter(SafeEcKeyHandle key, ReadOnlySpan<byte> sha256, int len)
|
||||
{
|
||||
SafeBignumHandle kinv_bn, rp_bn;
|
||||
int kinvlen, rplen;
|
||||
|
||||
bool refAdded = false;
|
||||
try
|
||||
{
|
||||
key.DangerousAddRef(ref refAdded);
|
||||
var ret = EcPspParameter(key, ref MemoryMarshal.GetReference(sha256), len, out kinv_bn, out kinvlen,
|
||||
out rp_bn, out rplen);
|
||||
if (!ret)
|
||||
{
|
||||
throw Interop.Crypto.CreateOpenSslCryptographicException();
|
||||
}
|
||||
|
||||
using (kinv_bn)
|
||||
using (rp_bn)
|
||||
{
|
||||
var par = new PspParameter
|
||||
{
|
||||
Kinv = Crypto.ExtractBignum(kinv_bn, kinvlen),
|
||||
Rp = Crypto.ExtractBignum(rp_bn, rplen)
|
||||
};
|
||||
return par;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (refAdded)
|
||||
key.DangerousRelease();
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcPspParameter")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool EcPspParameter(SafeEcKeyHandle ecKey, ref byte sha256, int len, out SafeBignumHandle kinv,
|
||||
out int kinvlen, out SafeBignumHandle rp, out int rplen);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class Crypto
|
||||
{
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyDestroy")]
|
||||
internal static extern void EcKeyDestroy(IntPtr a);
|
||||
|
||||
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyUpRef")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EcKeyUpRef(IntPtr r);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,231 @@
|
|||
using System;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
|
||||
public static class KeyVault
|
||||
{
|
||||
// KIRK AES keys
|
||||
public static readonly byte[][] kirkKeys =
|
||||
{
|
||||
new byte[] {0x2C, 0x92, 0xE5, 0x90, 0x2B, 0x86, 0xC1, 0x06, 0xB7, 0x2E, 0xEA, 0x6C, 0xD4, 0xEC, 0x72, 0x48},
|
||||
new byte[] {0x05, 0x8D, 0xC8, 0x0B, 0x33, 0xA5, 0xBF, 0x9D, 0x56, 0x98, 0xFA, 0xE0, 0xD3, 0x71, 0x5E, 0x1F},
|
||||
new byte[] {0xB8, 0x13, 0xC3, 0x5E, 0xC6, 0x44, 0x41, 0xE3, 0xDC, 0x3C, 0x16, 0xF5, 0xB4, 0x5E, 0x64, 0x84},
|
||||
new byte[] {0x98, 0x02, 0xC4, 0xE6, 0xEC, 0x9E, 0x9E, 0x2F, 0xFC, 0x63, 0x4C, 0xE4, 0x2F, 0xBB, 0x46, 0x68},
|
||||
new byte[] {0x99, 0x24, 0x4C, 0xD2, 0x58, 0xF5, 0x1B, 0xCB, 0xB0, 0x61, 0x9C, 0xA7, 0x38, 0x30, 0x07, 0x5F},
|
||||
new byte[] {0x02, 0x25, 0xD7, 0xBA, 0x63, 0xEC, 0xB9, 0x4A, 0x9D, 0x23, 0x76, 0x01, 0xB3, 0xF6, 0xAC, 0x17},
|
||||
new byte[] {0x60, 0x99, 0xF2, 0x81, 0x70, 0x56, 0x0E, 0x5F, 0x74, 0x7C, 0xB5, 0x20, 0xC0, 0xCD, 0xC2, 0x3C},
|
||||
new byte[] {0x76, 0x36, 0x8B, 0x43, 0x8F, 0x77, 0xD8, 0x7E, 0xFE, 0x5F, 0xB6, 0x11, 0x59, 0x39, 0x88, 0x5C},
|
||||
new byte[] {0x14, 0xA1, 0x15, 0xEB, 0x43, 0x4A, 0x1B, 0xA4, 0x90, 0x5E, 0x03, 0xB6, 0x17, 0xA1, 0x5C, 0x04},
|
||||
new byte[] {0xE6, 0x58, 0x03, 0xD9, 0xA7, 0x1A, 0xA8, 0x7F, 0x05, 0x9D, 0x22, 0x9D, 0xAF, 0x54, 0x53, 0xD0},
|
||||
new byte[] {0xBA, 0x34, 0x80, 0xB4, 0x28, 0xA7, 0xCA, 0x5F, 0x21, 0x64, 0x12, 0xF7, 0x0F, 0xBB, 0x73, 0x23},
|
||||
new byte[] {0x72, 0xAD, 0x35, 0xAC, 0x9A, 0xC3, 0x13, 0x0A, 0x77, 0x8C, 0xB1, 0x9D, 0x88, 0x55, 0x0B, 0x0C},
|
||||
new byte[] {0x84, 0x85, 0xC8, 0x48, 0x75, 0x08, 0x43, 0xBC, 0x9B, 0x9A, 0xEC, 0xA7, 0x9C, 0x7F, 0x60, 0x18},
|
||||
new byte[] {0xB5, 0xB1, 0x6E, 0xDE, 0x23, 0xA9, 0x7B, 0x0E, 0xA1, 0x7C, 0xDB, 0xA2, 0xDC, 0xDE, 0xC4, 0x6E},
|
||||
new byte[] {0xC8, 0x71, 0xFD, 0xB3, 0xBC, 0xC5, 0xD2, 0xF2, 0xE2, 0xD7, 0x72, 0x9D, 0xDF, 0x82, 0x68, 0x82},
|
||||
new byte[] {0x0A, 0xBB, 0x33, 0x6C, 0x96, 0xD4, 0xCD, 0xD8, 0xCB, 0x5F, 0x4B, 0xE0, 0xBA, 0xDB, 0x9E, 0x03},
|
||||
new byte[] {0x32, 0x29, 0x5B, 0xD5, 0xEA, 0xF7, 0xA3, 0x42, 0x16, 0xC8, 0x8E, 0x48, 0xFF, 0x50, 0xD3, 0x71},
|
||||
new byte[] {0x46, 0xF2, 0x5E, 0x8E, 0x4D, 0x2A, 0xA5, 0x40, 0x73, 0x0B, 0xC4, 0x6E, 0x47, 0xEE, 0x6F, 0x0A},
|
||||
new byte[] {0x5D, 0xC7, 0x11, 0x39, 0xD0, 0x19, 0x38, 0xBC, 0x02, 0x7F, 0xDD, 0xDC, 0xB0, 0x83, 0x7D, 0x9D},
|
||||
new byte[] {0x51, 0xDD, 0x65, 0xF0, 0x71, 0xA4, 0xE5, 0xEA, 0x6A, 0xAF, 0x12, 0x19, 0x41, 0x29, 0xB8, 0xF4},
|
||||
new byte[] {0x03, 0x76, 0x3C, 0x68, 0x65, 0xC6, 0x9B, 0x0F, 0xFE, 0x8F, 0xD8, 0xEE, 0xA4, 0x36, 0x16, 0xA0},
|
||||
new byte[] {0x7D, 0x50, 0xB8, 0x5C, 0xAF, 0x67, 0x69, 0xF0, 0xE5, 0x4A, 0xA8, 0x09, 0x8B, 0x0E, 0xBE, 0x1C},
|
||||
new byte[] {0x72, 0x68, 0x4B, 0x32, 0xAC, 0x3B, 0x33, 0x2F, 0x2A, 0x7A, 0xFC, 0x9E, 0x14, 0xD5, 0x6F, 0x6B},
|
||||
new byte[] {0x20, 0x1D, 0x31, 0x96, 0x4A, 0xD9, 0x9F, 0xBF, 0x32, 0xD5, 0xD6, 0x1C, 0x49, 0x1B, 0xD9, 0xFC},
|
||||
new byte[] {0xF8, 0xD8, 0x44, 0x63, 0xD6, 0x10, 0xD1, 0x2A, 0x44, 0x8E, 0x96, 0x90, 0xA6, 0xBB, 0x0B, 0xAD},
|
||||
new byte[] {0x5C, 0xD4, 0x05, 0x7F, 0xA1, 0x30, 0x60, 0x44, 0x0A, 0xD9, 0xB6, 0x74, 0x5F, 0x24, 0x4F, 0x4E},
|
||||
new byte[] {0xF4, 0x8A, 0xD6, 0x78, 0x59, 0x9C, 0x22, 0xC1, 0xD4, 0x11, 0x93, 0x3D, 0xF8, 0x45, 0xB8, 0x93},
|
||||
new byte[] {0xCA, 0xE7, 0xD2, 0x87, 0xA2, 0xEC, 0xC1, 0xCD, 0x94, 0x54, 0x2B, 0x5E, 0x1D, 0x94, 0x88, 0xB2},
|
||||
new byte[] {0xDE, 0x26, 0xD3, 0x7A, 0x39, 0x95, 0x6C, 0x2A, 0xD8, 0xC3, 0xA6, 0xAF, 0x21, 0xEB, 0xB3, 0x01},
|
||||
new byte[] {0x7C, 0xB6, 0x8B, 0x4D, 0xA3, 0x8D, 0x1D, 0xD9, 0x32, 0x67, 0x9C, 0xA9, 0x9F, 0xFB, 0x28, 0x52},
|
||||
new byte[] {0xA0, 0xB5, 0x56, 0xB4, 0x69, 0xAB, 0x36, 0x8F, 0x36, 0xDE, 0xC9, 0x09, 0x2E, 0xCB, 0x41, 0xB1},
|
||||
new byte[] {0x93, 0x9D, 0xE1, 0x9B, 0x72, 0x5F, 0xEE, 0xE2, 0x45, 0x2A, 0xBC, 0x17, 0x06, 0xD1, 0x47, 0x69},
|
||||
new byte[] {0xA4, 0xA4, 0xE6, 0x21, 0x38, 0x2E, 0xF1, 0xAF, 0x7B, 0x17, 0x7A, 0xE8, 0x42, 0xAD, 0x00, 0x31},
|
||||
new byte[] {0xC3, 0x7F, 0x13, 0xE8, 0xCF, 0x84, 0xDB, 0x34, 0x74, 0x7B, 0xC3, 0xA0, 0xF1, 0x9D, 0x3A, 0x73},
|
||||
new byte[] {0x2B, 0xF7, 0x83, 0x8A, 0xD8, 0x98, 0xE9, 0x5F, 0xA5, 0xF9, 0x01, 0xDA, 0x61, 0xFE, 0x35, 0xBB},
|
||||
new byte[] {0xC7, 0x04, 0x62, 0x1E, 0x71, 0x4A, 0x66, 0xEA, 0x62, 0xE0, 0x4B, 0x20, 0x3D, 0xB8, 0xC2, 0xE5},
|
||||
new byte[] {0xC9, 0x33, 0x85, 0x9A, 0xAB, 0x00, 0xCD, 0xCE, 0x4D, 0x8B, 0x8E, 0x9F, 0x3D, 0xE6, 0xC0, 0x0F},
|
||||
new byte[] {0x18, 0x42, 0x56, 0x1F, 0x2B, 0x5F, 0x34, 0xE3, 0x51, 0x3E, 0xB7, 0x89, 0x77, 0x43, 0x1A, 0x65},
|
||||
new byte[] {0xDC, 0xB0, 0xA0, 0x06, 0x5A, 0x50, 0xA1, 0x4E, 0x59, 0xAC, 0x97, 0x3F, 0x17, 0x58, 0xA3, 0xA3},
|
||||
new byte[] {0xC4, 0xDB, 0xAE, 0x83, 0xE2, 0x9C, 0xF2, 0x54, 0xA3, 0xDD, 0x37, 0x4E, 0x80, 0x7B, 0xF4, 0x25},
|
||||
new byte[] {0xBF, 0xAE, 0xEB, 0x49, 0x82, 0x65, 0xC5, 0x7C, 0x64, 0xB8, 0xC1, 0x7E, 0x19, 0x06, 0x44, 0x09},
|
||||
new byte[] {0x79, 0x7C, 0xEC, 0xC3, 0xB3, 0xEE, 0x0A, 0xC0, 0x3B, 0xD8, 0xE6, 0xC1, 0xE0, 0xA8, 0xB1, 0xA4},
|
||||
new byte[] {0x75, 0x34, 0xFE, 0x0B, 0xD6, 0xD0, 0xC2, 0x8D, 0x68, 0xD4, 0xE0, 0x2A, 0xE7, 0xD5, 0xD1, 0x55},
|
||||
new byte[] {0xFA, 0xB3, 0x53, 0x26, 0x97, 0x4F, 0x4E, 0xDF, 0xE4, 0xC3, 0xA8, 0x14, 0xC3, 0x2F, 0x0F, 0x88},
|
||||
new byte[] {0xEC, 0x97, 0xB3, 0x86, 0xB4, 0x33, 0xC6, 0xBF, 0x4E, 0x53, 0x9D, 0x95, 0xEB, 0xB9, 0x79, 0xE4},
|
||||
new byte[] {0xB3, 0x20, 0xA2, 0x04, 0xCF, 0x48, 0x06, 0x29, 0xB5, 0xDD, 0x8E, 0xFC, 0x98, 0xD4, 0x17, 0x7B},
|
||||
new byte[] {0x5D, 0xFC, 0x0D, 0x4F, 0x2C, 0x39, 0xDA, 0x68, 0x4A, 0x33, 0x74, 0xED, 0x49, 0x58, 0xA7, 0x3A},
|
||||
new byte[] {0xD7, 0x5A, 0x54, 0x22, 0xCE, 0xD9, 0xA3, 0xD6, 0x2B, 0x55, 0x7D, 0x8D, 0xE8, 0xBE, 0xC7, 0xEC},
|
||||
new byte[] {0x6B, 0x4A, 0xEE, 0x43, 0x45, 0xAE, 0x70, 0x07, 0xCF, 0x8D, 0xCF, 0x4E, 0x4A, 0xE9, 0x3C, 0xFA},
|
||||
new byte[] {0x2B, 0x52, 0x2F, 0x66, 0x4C, 0x2D, 0x11, 0x4C, 0xFE, 0x61, 0x31, 0x8C, 0x56, 0x78, 0x4E, 0xA6},
|
||||
new byte[] {0x3A, 0xA3, 0x4E, 0x44, 0xC6, 0x6F, 0xAF, 0x7B, 0xFA, 0xE5, 0x53, 0x27, 0xEF, 0xCF, 0xCC, 0x24},
|
||||
new byte[] {0x2B, 0x5C, 0x78, 0xBF, 0xC3, 0x8E, 0x49, 0x9D, 0x41, 0xC3, 0x3C, 0x5C, 0x7B, 0x27, 0x96, 0xCE},
|
||||
new byte[] {0xF3, 0x7E, 0xEA, 0xD2, 0xC0, 0xC8, 0x23, 0x1D, 0xA9, 0x9B, 0xFA, 0x49, 0x5D, 0xB7, 0x08, 0x1B},
|
||||
new byte[] {0x70, 0x8D, 0x4E, 0x6F, 0xD1, 0xF6, 0x6F, 0x1D, 0x1E, 0x1F, 0xCB, 0x02, 0xF9, 0xB3, 0x99, 0x26},
|
||||
new byte[] {0x0F, 0x67, 0x16, 0xE1, 0x80, 0x69, 0x9C, 0x51, 0xFC, 0xC7, 0xAD, 0x6E, 0x4F, 0xB8, 0x46, 0xC9},
|
||||
new byte[] {0x56, 0x0A, 0x49, 0x4A, 0x84, 0x4C, 0x8E, 0xD9, 0x82, 0xEE, 0x0B, 0x6D, 0xC5, 0x7D, 0x20, 0x8D},
|
||||
new byte[] {0x12, 0x46, 0x8D, 0x7E, 0x1C, 0x42, 0x20, 0x9B, 0xBA, 0x54, 0x26, 0x83, 0x5E, 0xB0, 0x33, 0x03},
|
||||
new byte[] {0xC4, 0x3B, 0xB6, 0xD6, 0x53, 0xEE, 0x67, 0x49, 0x3E, 0xA9, 0x5F, 0xBC, 0x0C, 0xED, 0x6F, 0x8A},
|
||||
new byte[] {0x2C, 0xC3, 0xCF, 0x8C, 0x28, 0x78, 0xA5, 0xA6, 0x63, 0xE2, 0xAF, 0x2D, 0x71, 0x5E, 0x86, 0xBA},
|
||||
new byte[] {0x83, 0x3D, 0xA7, 0x0C, 0xED, 0x6A, 0x20, 0x12, 0xD1, 0x96, 0xE6, 0xFE, 0x5C, 0x4D, 0x37, 0xC5},
|
||||
new byte[] {0xC7, 0x43, 0xD0, 0x67, 0x42, 0xEE, 0x90, 0xB8, 0xCA, 0x75, 0x50, 0x35, 0x20, 0xAD, 0xBC, 0xCE},
|
||||
new byte[] {0x8A, 0xE3, 0x66, 0x3F, 0x8D, 0x9E, 0x82, 0xA1, 0xED, 0xE6, 0x8C, 0x9C, 0xE8, 0x25, 0x6D, 0xAA},
|
||||
new byte[] {0x7F, 0xC9, 0x6F, 0x0B, 0xB1, 0x48, 0x5C, 0xA5, 0x5D, 0xD3, 0x64, 0xB7, 0x7A, 0xF5, 0xE4, 0xEA},
|
||||
new byte[] {0x91, 0xB7, 0x65, 0x78, 0x8B, 0xCB, 0x8B, 0xD4, 0x02, 0xED, 0x55, 0x3A, 0x66, 0x62, 0xD0, 0xAD},
|
||||
new byte[] {0x28, 0x24, 0xF9, 0x10, 0x1B, 0x8D, 0x0F, 0x7B, 0x6E, 0xB2, 0x63, 0xB5, 0xB5, 0x5B, 0x2E, 0xBB},
|
||||
new byte[] {0x30, 0xE2, 0x57, 0x5D, 0xE0, 0xA2, 0x49, 0xCE, 0xE8, 0xCF, 0x2B, 0x5E, 0x4D, 0x9F, 0x52, 0xC7},
|
||||
new byte[] {0x5E, 0xE5, 0x04, 0x39, 0x62, 0x32, 0x02, 0xFA, 0x85, 0x39, 0x3F, 0x72, 0xBB, 0x77, 0xFD, 0x1A},
|
||||
new byte[] {0xF8, 0x81, 0x74, 0xB1, 0xBD, 0xE9, 0xBF, 0xDD, 0x45, 0xE2, 0xF5, 0x55, 0x89, 0xCF, 0x46, 0xAB},
|
||||
new byte[] {0x7D, 0xF4, 0x92, 0x65, 0xE3, 0xFA, 0xD6, 0x78, 0xD6, 0xFE, 0x78, 0xAD, 0xBB, 0x3D, 0xFB, 0x63},
|
||||
new byte[] {0x74, 0x7F, 0xD6, 0x2D, 0xC7, 0xA1, 0xCA, 0x96, 0xE2, 0x7A, 0xCE, 0xFF, 0xAA, 0x72, 0x3F, 0xF7},
|
||||
new byte[] {0x1E, 0x58, 0xEB, 0xD0, 0x65, 0xBB, 0xF1, 0x68, 0xC5, 0xBD, 0xF7, 0x46, 0xBA, 0x7B, 0xE1, 0x00},
|
||||
new byte[] {0x24, 0x34, 0x7D, 0xAF, 0x5E, 0x4B, 0x35, 0x72, 0x7A, 0x52, 0x27, 0x6B, 0xA0, 0x54, 0x74, 0xDB},
|
||||
new byte[] {0x09, 0xB1, 0xC7, 0x05, 0xC3, 0x5F, 0x53, 0x66, 0x77, 0xC0, 0xEB, 0x36, 0x77, 0xDF, 0x83, 0x07},
|
||||
new byte[] {0xCC, 0xBE, 0x61, 0x5C, 0x05, 0xA2, 0x00, 0x33, 0x37, 0x8E, 0x59, 0x64, 0xA7, 0xDD, 0x70, 0x3D},
|
||||
new byte[] {0x0D, 0x47, 0x50, 0xBB, 0xFC, 0xB0, 0x02, 0x81, 0x30, 0xE1, 0x84, 0xDE, 0xA8, 0xD4, 0x84, 0x13},
|
||||
new byte[] {0x0C, 0xFD, 0x67, 0x9A, 0xF9, 0xB4, 0x72, 0x4F, 0xD7, 0x8D, 0xD6, 0xE9, 0x96, 0x42, 0x28, 0x8B},
|
||||
new byte[] {0x7A, 0xD3, 0x1A, 0x8B, 0x4B, 0xEF, 0xC2, 0xC2, 0xB3, 0x99, 0x01, 0xA9, 0xFE, 0x76, 0xB9, 0x87},
|
||||
new byte[] {0xBE, 0x78, 0x78, 0x17, 0xC7, 0xF1, 0x6F, 0x1A, 0xE0, 0xEF, 0x3B, 0xDE, 0x4C, 0xC2, 0xD7, 0x86},
|
||||
new byte[] {0x7C, 0xD8, 0xB8, 0x91, 0x91, 0x0A, 0x43, 0x14, 0xD0, 0x53, 0x3D, 0xD8, 0x4C, 0x45, 0xBE, 0x16},
|
||||
new byte[] {0x32, 0x72, 0x2C, 0x88, 0x07, 0xCF, 0x35, 0x7D, 0x4A, 0x2F, 0x51, 0x19, 0x44, 0xAE, 0x68, 0xDA},
|
||||
new byte[] {0x7E, 0x6B, 0xBF, 0xF6, 0xF6, 0x87, 0xB8, 0x98, 0xEE, 0xB5, 0x1B, 0x32, 0x16, 0xE4, 0x6E, 0x5D},
|
||||
new byte[] {0x08, 0xEA, 0x5A, 0x83, 0x49, 0xB5, 0x9D, 0xB5, 0x3E, 0x07, 0x79, 0xB1, 0x9A, 0x59, 0xA3, 0x54},
|
||||
new byte[] {0xF3, 0x12, 0x81, 0xBF, 0xE6, 0x9F, 0x51, 0xD1, 0x64, 0x08, 0x25, 0x21, 0xFF, 0xBB, 0x22, 0x61},
|
||||
new byte[] {0xAF, 0xFE, 0x8E, 0xB1, 0x3D, 0xD1, 0x7E, 0xD8, 0x0A, 0x61, 0x24, 0x1C, 0x95, 0x92, 0x56, 0xB6},
|
||||
new byte[] {0x92, 0xCD, 0xB4, 0xC2, 0x5B, 0xF2, 0x35, 0x5A, 0x23, 0x09, 0xE8, 0x19, 0xC9, 0x14, 0x42, 0x35},
|
||||
new byte[] {0xE1, 0xC6, 0x5B, 0x22, 0x6B, 0xE1, 0xDA, 0x02, 0xBA, 0x18, 0xFA, 0x21, 0x34, 0x9E, 0xF9, 0x6D},
|
||||
new byte[] {0x14, 0xEC, 0x76, 0xCE, 0x97, 0xF3, 0x8A, 0x0A, 0x34, 0x50, 0x6C, 0x53, 0x9A, 0x5C, 0x9A, 0xB4},
|
||||
new byte[] {0x1C, 0x9B, 0xC4, 0x90, 0xE3, 0x06, 0x64, 0x81, 0xFA, 0x59, 0xFD, 0xB6, 0x00, 0xBB, 0x28, 0x70},
|
||||
new byte[] {0x43, 0xA5, 0xCA, 0xCC, 0x0D, 0x6C, 0x2D, 0x3F, 0x2B, 0xD9, 0x89, 0x67, 0x6B, 0x3F, 0x7F, 0x57},
|
||||
new byte[] {0x00, 0xEF, 0xFD, 0x18, 0x08, 0xA4, 0x05, 0x89, 0x3C, 0x38, 0xFB, 0x25, 0x72, 0x70, 0x61, 0x06},
|
||||
new byte[] {0xEE, 0xAF, 0x49, 0xE0, 0x09, 0x87, 0x9B, 0xEF, 0xAA, 0xD6, 0x32, 0x6A, 0x32, 0x13, 0xC4, 0x29},
|
||||
new byte[] {0x8D, 0x26, 0xB9, 0x0F, 0x43, 0x1D, 0xBB, 0x08, 0xDB, 0x1D, 0xDA, 0xC5, 0xB5, 0x2C, 0x92, 0xED},
|
||||
new byte[] {0x57, 0x7C, 0x30, 0x60, 0xAE, 0x6E, 0xBE, 0xAE, 0x3A, 0xAB, 0x18, 0x19, 0xC5, 0x71, 0x68, 0x0B},
|
||||
new byte[] {0x11, 0x5A, 0x5D, 0x20, 0xD5, 0x3A, 0x8D, 0xD3, 0x9C, 0xC5, 0xAF, 0x41, 0x0F, 0x0F, 0x18, 0x6F},
|
||||
new byte[] {0x0D, 0x4D, 0x51, 0xAB, 0x23, 0x79, 0xBF, 0x80, 0x3A, 0xBF, 0xB9, 0x0E, 0x75, 0xFC, 0x14, 0xBF},
|
||||
new byte[] {0x99, 0x93, 0xDA, 0x3E, 0x7D, 0x2E, 0x5B, 0x15, 0xF2, 0x52, 0xA4, 0xE6, 0x6B, 0xB8, 0x5A, 0x98},
|
||||
new byte[] {0xF4, 0x28, 0x30, 0xA5, 0xFB, 0x0D, 0x8D, 0x76, 0x0E, 0xA6, 0x71, 0xC2, 0x2B, 0xDE, 0x66, 0x9D},
|
||||
new byte[] {0xFB, 0x5F, 0xEB, 0x7F, 0xC7, 0xDC, 0xDD, 0x69, 0x37, 0x01, 0x97, 0x9B, 0x29, 0x03, 0x5C, 0x47},
|
||||
new byte[] {0x02, 0x32, 0x6A, 0xE7, 0xD3, 0x96, 0xCE, 0x7F, 0x1C, 0x41, 0x9D, 0xD6, 0x52, 0x07, 0xED, 0x09},
|
||||
new byte[] {0x9C, 0x9B, 0x13, 0x72, 0xF8, 0xC6, 0x40, 0xCF, 0x1C, 0x62, 0xF5, 0xD5, 0x92, 0xDD, 0xB5, 0x82},
|
||||
new byte[] {0x03, 0xB3, 0x02, 0xE8, 0x5F, 0xF3, 0x81, 0xB1, 0x3B, 0x8D, 0xAA, 0x2A, 0x90, 0xFF, 0x5E, 0x61},
|
||||
new byte[] {0xBC, 0xD7, 0xF9, 0xD3, 0x2F, 0xAC, 0xF8, 0x47, 0xC0, 0xFB, 0x4D, 0x2F, 0x30, 0x9A, 0xBD, 0xA6},
|
||||
new byte[] {0xF5, 0x55, 0x96, 0xE9, 0x7F, 0xAF, 0x86, 0x7F, 0xAC, 0xB3, 0x3A, 0xE6, 0x9C, 0x8B, 0x6F, 0x93},
|
||||
new byte[] {0xEE, 0x29, 0x70, 0x93, 0xF9, 0x4E, 0x44, 0x59, 0x44, 0x17, 0x1F, 0x8E, 0x86, 0xE1, 0x70, 0xFC},
|
||||
new byte[] {0xE4, 0x34, 0x52, 0x0C, 0xF0, 0x88, 0xCF, 0xC8, 0xCD, 0x78, 0x1B, 0x6C, 0xCF, 0x8C, 0x48, 0xC4},
|
||||
new byte[] {0xC1, 0xBF, 0x66, 0x81, 0x8E, 0xF9, 0x53, 0xF2, 0xE1, 0x26, 0x6B, 0x6F, 0x55, 0x0C, 0xC9, 0xCD},
|
||||
new byte[] {0x56, 0x0F, 0xFF, 0x8F, 0x3C, 0x96, 0x49, 0x14, 0x45, 0x16, 0xF1, 0xBC, 0xBF, 0xCE, 0xA3, 0x0C},
|
||||
new byte[] {0x24, 0x08, 0xDC, 0x75, 0x37, 0x60, 0xA2, 0x9F, 0x05, 0x54, 0xB5, 0xF2, 0x43, 0x85, 0x73, 0x99},
|
||||
new byte[] {0xDD, 0xD5, 0xB5, 0x6A, 0x59, 0xC5, 0x5A, 0xE8, 0x3B, 0x96, 0x67, 0xC7, 0x5C, 0x2A, 0xE2, 0xDC},
|
||||
new byte[] {0xAA, 0x68, 0x67, 0x72, 0xE0, 0x2D, 0x44, 0xD5, 0xCD, 0xBB, 0x65, 0x04, 0xBC, 0xD5, 0xBF, 0x4E},
|
||||
new byte[] {0x1F, 0x17, 0xF0, 0x14, 0xE7, 0x77, 0xA2, 0xFE, 0x4B, 0x13, 0x6B, 0x56, 0xCD, 0x7E, 0xF7, 0xE9},
|
||||
new byte[] {0xC9, 0x35, 0x48, 0xCF, 0x55, 0x8D, 0x75, 0x03, 0x89, 0x6B, 0x2E, 0xEB, 0x61, 0x8C, 0xA9, 0x02},
|
||||
new byte[] {0xDE, 0x34, 0xC5, 0x41, 0xE7, 0xCA, 0x86, 0xE8, 0xBE, 0xA7, 0xC3, 0x1C, 0xEC, 0xE4, 0x36, 0x0F},
|
||||
new byte[] {0xDD, 0xE5, 0xFF, 0x55, 0x1B, 0x74, 0xF6, 0xF4, 0xE0, 0x16, 0xD7, 0xAB, 0x22, 0x31, 0x1B, 0x6A},
|
||||
new byte[] {0xB0, 0xE9, 0x35, 0x21, 0x33, 0x3F, 0xD7, 0xBA, 0xB4, 0x76, 0x2C, 0xCB, 0x4D, 0x80, 0x08, 0xD8},
|
||||
new byte[] {0x38, 0x14, 0x69, 0xC4, 0xC3, 0xF9, 0x1B, 0x96, 0x33, 0x63, 0x8E, 0x4D, 0x5F, 0x3D, 0xF0, 0x29},
|
||||
new byte[] {0xFA, 0x48, 0x6A, 0xD9, 0x8E, 0x67, 0x16, 0xEF, 0x6A, 0xB0, 0x87, 0xF5, 0x89, 0x45, 0x7F, 0x2A},
|
||||
new byte[] {0x32, 0x1A, 0x09, 0x12, 0x50, 0x14, 0x8A, 0x3E, 0x96, 0x3D, 0xEA, 0x02, 0x59, 0x32, 0xE1, 0x8F},
|
||||
new byte[] {0x4B, 0x00, 0xBE, 0x29, 0xBC, 0xB0, 0x28, 0x64, 0xCE, 0xFD, 0x43, 0xA9, 0x6F, 0xD9, 0x5C, 0xED},
|
||||
new byte[] {0x57, 0x7D, 0xC4, 0xFF, 0x02, 0x44, 0xE2, 0x80, 0x91, 0xF4, 0xCA, 0x0A, 0x75, 0x69, 0xFD, 0xA8},
|
||||
new byte[] {0x83, 0x53, 0x36, 0xC6, 0x18, 0x03, 0xE4, 0x3E, 0x4E, 0xB3, 0x0F, 0x6B, 0x6E, 0x79, 0x9B, 0x7A},
|
||||
new byte[] {0x5C, 0x92, 0x65, 0xFD, 0x7B, 0x59, 0x6A, 0xA3, 0x7A, 0x2F, 0x50, 0x9D, 0x85, 0xE9, 0x27, 0xF8},
|
||||
new byte[] {0x9A, 0x39, 0xFB, 0x89, 0xDF, 0x55, 0xB2, 0x60, 0x14, 0x24, 0xCE, 0xA6, 0xD9, 0x65, 0x0A, 0x9D},
|
||||
new byte[] {0x8B, 0x75, 0xBE, 0x91, 0xA8, 0xC7, 0x5A, 0xD2, 0xD7, 0xA5, 0x94, 0xA0, 0x1C, 0xBB, 0x95, 0x91},
|
||||
new byte[] {0x95, 0xC2, 0x1B, 0x8D, 0x05, 0xAC, 0xF5, 0xEC, 0x5A, 0xEE, 0x77, 0x81, 0x23, 0x95, 0xC4, 0xD7},
|
||||
new byte[] {0xB9, 0xA4, 0x61, 0x64, 0x36, 0x33, 0xFA, 0x5D, 0x94, 0x88, 0xE2, 0xD3, 0x28, 0x1E, 0x01, 0xA2},
|
||||
new byte[] {0xB8, 0xB0, 0x84, 0xFB, 0x9F, 0x4C, 0xFA, 0xF7, 0x30, 0xFE, 0x73, 0x25, 0xA2, 0xAB, 0x89, 0x7D},
|
||||
new byte[] {0x5F, 0x8C, 0x17, 0x9F, 0xC1, 0xB2, 0x1D, 0xF1, 0xF6, 0x36, 0x7A, 0x9C, 0xF7, 0xD3, 0xD4, 0x7C},
|
||||
};
|
||||
|
||||
public static readonly byte[] kirk1_key = { 0x98, 0xC9, 0x40, 0x97, 0x5C, 0x1D, 0x10, 0xE8, 0x7F, 0xE6, 0x0E, 0xA3, 0xFD, 0x03, 0xA8, 0xBA };
|
||||
public static readonly byte[] kirk16_key = { 0x47, 0x5E, 0x09, 0xF4, 0xA2, 0x37, 0xDA, 0x9B, 0xEF, 0xFF, 0x3B, 0xC0, 0x77, 0x14, 0x3D, 0x8A };
|
||||
|
||||
/* ECC Curves for Kirk 1 and Kirk 0x11 */
|
||||
// Common Curve paramters p and a
|
||||
public static readonly byte[] ec_p = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
public static readonly byte[] ec_a = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC }; // mon
|
||||
|
||||
// Kirk 0xC,0xD,0x10,0x11,(likely 0x12)- Unique curve parameters for b, N, and base point G for Kirk 0xC,0xD,0x10,0x11,(likely 0x12) service
|
||||
// Since public key is variable, it is not specified here
|
||||
public static readonly byte[] ec_b2 = { 0xA6, 0x8B, 0xED, 0xC3, 0x34, 0x18, 0x02, 0x9C, 0x1D, 0x3C, 0xE3, 0x3B, 0x9A, 0x32, 0x1F, 0xCC, 0xBB, 0x9E, 0x0F, 0x0B };// mon
|
||||
public static readonly byte[] ec_N2 = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xB5, 0xAE, 0x3C, 0x52, 0x3E, 0x63, 0x94, 0x4F, 0x21, 0x27 };
|
||||
public static readonly byte[] Gx2 = { 0x12, 0x8E, 0xC4, 0x25, 0x64, 0x87, 0xFD, 0x8F, 0xDF, 0x64, 0xE2, 0x43, 0x7B, 0xC0, 0xA1, 0xF6, 0xD5, 0xAF, 0xDE, 0x2C };
|
||||
public static readonly byte[] Gy2 = { 0x59, 0x58, 0x55, 0x7E, 0xB1, 0xDB, 0x00, 0x12, 0x60, 0x42, 0x55, 0x24, 0xDB, 0xC3, 0x79, 0xD5, 0xAC, 0x5F, 0x4A, 0xDF };
|
||||
|
||||
// KIRK 1 - Unique curve parameters for b, N, and base point G
|
||||
// Since public key is hard coded, it is also included
|
||||
public static readonly byte[] ec_b1 = { 0x65, 0xD1, 0x48, 0x8C, 0x03, 0x59, 0xE2, 0x34, 0xAD, 0xC9, 0x5B, 0xD3, 0x90, 0x80, 0x14, 0xBD, 0x91, 0xA5, 0x25, 0xF9 };
|
||||
public static readonly byte[] ec_N1 = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xB5, 0xC6, 0x17, 0xF2, 0x90, 0xEA, 0xE1, 0xDB, 0xAD, 0x8F };
|
||||
public static readonly byte[] Gx1 = { 0x22, 0x59, 0xAC, 0xEE, 0x15, 0x48, 0x9C, 0xB0, 0x96, 0xA8, 0x82, 0xF0, 0xAE, 0x1C, 0xF9, 0xFD, 0x8E, 0xE5, 0xF8, 0xFA };
|
||||
public static readonly byte[] Gy1 = { 0x60, 0x43, 0x58, 0x45, 0x6D, 0x0A, 0x1C, 0xB2, 0x90, 0x8D, 0xE9, 0x0F, 0x27, 0xD7, 0x5C, 0x82, 0xBE, 0xC1, 0x08, 0xC0 };
|
||||
|
||||
public static readonly byte[] Px1 = { 0xED, 0x9C, 0xE5, 0x82, 0x34, 0xE6, 0x1A, 0x53, 0xC6, 0x85, 0xD6, 0x4D, 0x51, 0xD0, 0x23, 0x6B, 0xC3, 0xB5, 0xD4, 0xB9 };
|
||||
public static readonly byte[] Py1 = { 0x04, 0x9D, 0xF1, 0xA0, 0x75, 0xC0, 0xE0, 0x4F, 0xB3, 0x44, 0x85, 0x8B, 0x61, 0xB7, 0x9B, 0x69, 0xA6, 0x3D, 0x2C, 0x39 };
|
||||
public static readonly byte[] ec_Priv1 = { 0xF3, 0x92, 0xE2, 0x64, 0x90, 0xB8, 0x0F, 0xD8, 0x89, 0xF2, 0xD9, 0x72, 0x2C, 0x1F, 0x34, 0xD7, 0x27, 0x4F, 0x98, 0x3D };
|
||||
|
||||
public static readonly byte[] Px2 = { 0x01, 0x21, 0xEA, 0x6E, 0xCD, 0xB2, 0x3A, 0x3E, 0x23, 0x75, 0x67, 0x1C, 0x53, 0x62, 0xE8, 0xE2, 0x8B, 0x1E, 0x78, 0x3B };
|
||||
public static readonly byte[] Py2 = { 0x1A, 0x27, 0x32, 0x15, 0x8B, 0x8C, 0xED, 0x98, 0x46, 0x6C, 0x18, 0xA3, 0xAC, 0x3B, 0x11, 0x06, 0xAF, 0xB4, 0xEC, 0x3B };
|
||||
public static readonly byte[] ec_Priv2 = { 0x14, 0xB0, 0x22, 0xE8, 0x92, 0xCF, 0x86, 0x14, 0xA4, 0x45, 0x57, 0xDB, 0x09, 0x5C, 0x92, 0x8D, 0xE9, 0xB8, 0x99, 0x70 };
|
||||
|
||||
public static readonly byte[] EdatPx = { 0x1F, 0x07, 0x2B, 0xCC, 0xC1, 0x62, 0xF2, 0xCF, 0xAE, 0xA0, 0xE7, 0xF4, 0xCD, 0xFD, 0x9C, 0xAE, 0xC6, 0xC4, 0x55, 0x21 };
|
||||
public static readonly byte[] EdatPy = { 0x53, 0x01, 0xF4, 0xE3, 0x70, 0xC3, 0xED, 0xE2, 0xD4, 0xF5, 0xDB, 0xC3, 0xA7, 0xDE, 0x8C, 0xAA, 0xE8, 0xAD, 0x5B, 0x7D };
|
||||
public static readonly byte[] EdatPirv = { 0xe5, 0xc4, 0xd0, 0xa8, 0x24, 0x9a, 0x6f, 0x27, 0xe5, 0xe0, 0xc9, 0xd5, 0x34, 0xf4, 0xda, 0x15, 0x22, 0x3f, 0x42, 0xad };
|
||||
|
||||
public static readonly byte[] Eboot_p = { 0xA5, 0x3E, 0x11, 0x3E, 0x46, 0xD8, 0xC9, 0xC1, 0xF0, 0x9D, 0x9B, 0xCB, 0x2A, 0x53, 0x73, 0xD3, 0x79, 0xF6, 0x9D, 0xA2, 0x8D, 0x09, 0x99, 0x9F, 0xED, 0x57, 0xA9, 0x0F };
|
||||
public static readonly byte[] Eboot_a = { 0xA5, 0x3E, 0x11, 0x3E, 0x46, 0xD8, 0xC9, 0xC1, 0xF0, 0x9D, 0x9B, 0xCB, 0x2A, 0x53, 0x73, 0xD3, 0x79, 0xF6, 0x9D, 0xA2, 0x8D, 0x09, 0x99, 0x9F, 0xED, 0x57, 0xA9, 0x0C };
|
||||
public static readonly byte[] Eboot_b = { 0x90, 0x65, 0x94, 0x1D, 0x29, 0x37, 0x4A, 0x8F, 0x11, 0xDD, 0x1E, 0x54, 0x01, 0x89, 0x43, 0x4E, 0x4A, 0x6E, 0xBF, 0xAF, 0x54, 0x77, 0xF6, 0xC1, 0x72, 0xF6, 0x85, 0x5E };
|
||||
public static readonly byte[] Eboot_N = { 0xA5, 0x3E, 0x11, 0x3E, 0x46, 0xD8, 0xC9, 0xC1, 0xF0, 0x9D, 0x9B, 0xCB, 0x2A, 0x52, 0x26, 0x98, 0xDE, 0xEF, 0x58, 0xDB, 0x1A, 0xD9, 0xAB, 0x7F, 0x04, 0xE3, 0xAE, 0x7F };
|
||||
public static readonly byte[] Eboot_Gx = { 0x7E, 0x06, 0x09, 0x82, 0x47, 0xE6, 0xB5, 0x9F, 0x31, 0x10, 0xBC, 0xBB, 0x3A, 0xB6, 0xC2, 0x50, 0xBC, 0x5A, 0xB0, 0x6C, 0x03, 0x2D, 0xAD, 0x43, 0x68, 0x4C, 0x24, 0x8F };
|
||||
public static readonly byte[] Eboot_Gy = { 0x0B, 0xD9, 0x41, 0x8D, 0xE8, 0xE3, 0xE4, 0x5D, 0x2D, 0x70, 0x1E, 0x02, 0x37, 0xFD, 0x7F, 0x2A, 0xDE, 0x0D, 0x48, 0xB7, 0x4C, 0xEE, 0xF2, 0xF1, 0xC8, 0xAC, 0x48, 0x4E };
|
||||
public static readonly byte[] Eboot_pub1x = { 0x5F, 0x9D, 0x17, 0x1A, 0x2B, 0xDD, 0xA8, 0xD4, 0x08, 0x78, 0xBF, 0x98, 0x5A, 0xC3, 0x26, 0xED, 0x5E, 0xFF, 0x43, 0xC9, 0x37, 0x6C, 0x77, 0xEC, 0x0A, 0x00, 0xC7, 0xBB };
|
||||
public static readonly byte[] Eboot_pub1y = { 0xA3, 0x44, 0xE4, 0x4E, 0x6E, 0xAC, 0x25, 0x52, 0x35, 0xF9, 0x54, 0xF5, 0xB6, 0x17, 0xC7, 0xBD, 0x49, 0xF1, 0x80, 0x26, 0x24, 0x54, 0xAA, 0xE1, 0xB6, 0x2A, 0x9F, 0x2C };
|
||||
public static readonly byte[] Eboot_priv1 = { 0x76, 0x74, 0x36, 0xA6, 0x99, 0x9D, 0x88, 0x48, 0x0E, 0xC8, 0x56, 0xF5, 0x5C, 0xEA, 0xBB, 0x43, 0x96, 0x85, 0x9E, 0x37, 0x45, 0x99, 0x40, 0x39, 0x21, 0xF5, 0x55, 0x98 };
|
||||
public static readonly byte[] Eboot_pub2x = { 0x67, 0x00, 0x2D, 0x9B, 0xB8, 0xE4, 0x2D, 0x2B, 0xF9, 0x61, 0x0B, 0x27, 0xFE, 0xAB, 0x9B, 0x34, 0x56, 0x15, 0x50, 0x92, 0x13, 0x12, 0xDF, 0xEE, 0x7A, 0x3A, 0x86, 0xEC };
|
||||
public static readonly byte[] Eboot_pub2y = { 0x6C, 0xA7, 0x14, 0x42, 0x6F, 0x6D, 0x4E, 0x96, 0x09, 0xA6, 0x38, 0xBF, 0x4A, 0xFB, 0x18, 0x2B, 0xFA, 0x50, 0xC8, 0x2F, 0xF2, 0xB4, 0xC5, 0xEC, 0x6C, 0xCD, 0x97, 0x65 };
|
||||
public static readonly byte[] Eboot_priv2 = { 0x60, 0x7A, 0x2E, 0x55, 0x68, 0xB4, 0xB9, 0xA0, 0x32, 0xF4, 0x52, 0x53, 0xCF, 0xED, 0x20, 0xDB, 0x2E, 0x6E, 0x44, 0x6C, 0x37, 0x82, 0xE8, 0x2A, 0x1A, 0xB9, 0xC9, 0x23 };
|
||||
public static readonly byte[][] Eboot_priv = { Eboot_priv1, Eboot_priv2 };
|
||||
public static readonly byte[][] Eboot_pubx = { Eboot_pub1x, Eboot_pub2x };
|
||||
public static readonly byte[][] Eboot_puby = { Eboot_pub1y, Eboot_pub2y };
|
||||
|
||||
public static readonly byte[] Eboot_hmacKey = { 0x54, 0x88, 0xA9, 0x81, 0x1C, 0x9A, 0x2C, 0xBC, 0xCC, 0x59, 0x6B, 0x1F, 0xAD, 0x1A, 0x7E, 0x29, 0xE0, 0x75, 0x84, 0x0F, 0x47, 0x43, 0x1F, 0x37, 0xAC, 0x06, 0x02, 0x46, 0x4A, 0x27, 0x9E, 0x02, 0xDF, 0x2E, 0x71, 0x65, 0xF1, 0x13, 0x7B, 0xF6, 0x9A, 0xE6, 0xDC, 0xB9, 0xDC, 0x38, 0x8C, 0x9D, 0xCC, 0xB3, 0x64, 0xC4, 0xCA, 0x26, 0xCB, 0x8F, 0x1A, 0xF0, 0x63, 0x8A, 0x6E, 0xAD, 0xB5, 0x4D };
|
||||
|
||||
|
||||
public static readonly byte[] VitaKirk18PubKey0x = { 0x5F, 0x9D, 0x17, 0x1A, 0x2B, 0xDD, 0xA8, 0xD4, 0x08, 0x78, 0xBF, 0x98, 0x5A, 0xC3, 0x26, 0xED, 0x5E, 0xFF, 0x43, 0xC9, 0x37, 0x6C, 0x77, 0xEC, 0x0A, 0x00, 0xC7, 0xBB };
|
||||
public static readonly byte[] VitaKirk18PubKey0y = { 0xA3, 0x44, 0xE4, 0x4E, 0x6E, 0xAC, 0x25, 0x52, 0x35, 0xF9, 0x54, 0xF5, 0xB6, 0x17, 0xC7, 0xBD, 0x49, 0xF1, 0x80, 0x26, 0x24, 0x54, 0xAA, 0xE1, 0xB6, 0x2A, 0x9F, 0x2C };
|
||||
|
||||
public static readonly byte[] VitaKirk18PubKey1x = { 0x67, 0x00, 0x2D, 0x9B, 0xB8, 0xE4, 0x2D, 0x2B, 0xF9, 0x61, 0x0B, 0x27, 0xFE, 0xAB, 0x9B, 0x34, 0x56, 0x15, 0x50, 0x92, 0x13, 0x12, 0xDF, 0xEE, 0x7A, 0x3A, 0x86, 0xEC };
|
||||
public static readonly byte[] VitaKirk18PubKey1y = { 0x6C, 0xA7, 0x14, 0x42, 0x6F, 0x6D, 0x4E, 0x96, 0x09, 0xA6, 0x38, 0xBF, 0x4A, 0xFB, 0x18, 0x2B, 0xFA, 0x50, 0xC8, 0x2F, 0xF2, 0xB4, 0xC5, 0xEC, 0x6C, 0xCD, 0x97, 0x65 };
|
||||
|
||||
public static readonly byte[] VitaKirk18PubKey1000x = { 0x64, 0xDD, 0xD3, 0x1E, 0x46, 0x7A, 0x90, 0x8F, 0x99, 0x0D, 0x63, 0xF4, 0x59, 0x32, 0x3C, 0x1C, 0xA3, 0xCE, 0xCF, 0x00, 0xA8, 0x1C, 0x57, 0x40, 0xA9, 0x6D, 0x2E, 0x1C };
|
||||
public static readonly byte[] VitaKirk18PubKey1000y = { 0x38, 0x61, 0xA1, 0x9D, 0x0A, 0xBD, 0xC5, 0xED, 0xDB, 0xD6, 0x04, 0xFA, 0x67, 0xC6, 0xDA, 0xB4, 0x28, 0x3C, 0x29, 0x67, 0x86, 0xD9, 0xA8, 0x07, 0xE0, 0xC1, 0x3B, 0xA8 };
|
||||
|
||||
public static readonly byte[] drmActRifSig =
|
||||
{
|
||||
0x62, 0x27, 0xB0, 0x0A, 0x02, 0x85, 0x6F, 0xB0,
|
||||
0x41, 0x08, 0x87, 0x67, 0x19, 0xE0, 0xA0, 0x18,
|
||||
0x32, 0x91, 0xEE, 0xB9, 0x6E, 0x73, 0x6A, 0xBF,
|
||||
0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE,
|
||||
0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
|
||||
};
|
||||
|
||||
public static readonly byte[] drmRifKey = { 0xDA, 0x7D, 0x4B, 0x5E, 0x49, 0x9A, 0x4F, 0x53, 0xB1, 0xC1, 0xA1, 0x4A, 0x74, 0x84, 0x44, 0x3B };
|
||||
|
||||
public static readonly byte[] drmActdatKey = { 0x5E, 0x06, 0xE0, 0x4F, 0xD9, 0x4A, 0x71, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
|
||||
|
||||
public static readonly byte[] drmVersionKeyKey =
|
||||
{
|
||||
0xF0, 0x79, 0xD5, 0x19, 0x8F, 0x23, 0xEF, 0xCE,
|
||||
0xB5, 0x4B, 0x9E, 0xCD, 0xCD, 0xFD, 0xD3, 0xD7,
|
||||
0x07, 0x3D, 0x9E, 0x9D, 0xA8, 0xFD, 0x3B, 0x2F,
|
||||
0x63, 0x18, 0x93, 0x2E, 0xF8, 0x57, 0xA6, 0x64,
|
||||
0x37, 0x49, 0xB7, 0x01, 0xCA, 0xE2, 0xE0, 0xC5,
|
||||
0x44, 0x2E, 0x06, 0xB6, 0x1E, 0xFF, 0x84, 0xF2,
|
||||
0x9D, 0x31, 0xB8, 0x5A, 0xC8, 0xFA, 0x16, 0x80,
|
||||
0x73, 0x60, 0x18, 0x82, 0x18, 0x77, 0x91, 0x9D,
|
||||
|
||||
};
|
||||
|
||||
public static readonly byte[] DrmFixedKey = { 0x38, 0x20, 0xD0, 0x11, 0x07, 0xA3, 0xFF, 0x3E, 0x0A, 0x4C, 0x20, 0x85, 0x39, 0x10, 0xB5, 0x54 };
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
internal static class Libraries
|
||||
{
|
||||
internal const string CryptoNative = "PspCryptoHelper";
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SevenZip.Compression.LZMA;
|
||||
using Decoder = SevenZip.Compression.LZMA.Decoder;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class Lz
|
||||
{
|
||||
public static byte[] compress(byte[] in_buf)
|
||||
{
|
||||
//Decoder decoder = new Decoder();
|
||||
//using var inStream = new MemoryStream(@in);
|
||||
//using var outStream = new MemoryStream(@out);
|
||||
//byte[] properties = new byte[5];
|
||||
//inStream.Read(properties, 0, 5);
|
||||
//decoder.SetDecoderProperties(properties);
|
||||
//decoder.Code(inStream, outStream, insize, size, null);
|
||||
//return 0;
|
||||
var lzrc = new Lzrc();
|
||||
|
||||
// create a buffer hopefully big enough to hold compression result
|
||||
byte[] compression_result = new byte[in_buf.Length + 0xffff]; // in worst case (i.e random data),
|
||||
// compression makes the data larger
|
||||
// so have to make sure theres extra space ..
|
||||
|
||||
// compress data, and get the compressed data length
|
||||
int compressed_length = lzrc.lzrc_compress(compression_result, compression_result.Length, in_buf, in_buf.Length);
|
||||
|
||||
// resize array to actual compressed length ...
|
||||
Array.Resize(ref compression_result, compressed_length);
|
||||
|
||||
return compression_result;
|
||||
}
|
||||
public static int decompress(byte[] @out, byte[] @in, int size, int insize)
|
||||
{
|
||||
//Decoder decoder = new Decoder();
|
||||
//using var inStream = new MemoryStream(@in);
|
||||
//using var outStream = new MemoryStream(@out);
|
||||
//byte[] properties = new byte[5];
|
||||
//inStream.Read(properties, 0, 5);
|
||||
//decoder.SetDecoderProperties(properties);
|
||||
//decoder.Code(inStream, outStream, insize, size, null);
|
||||
//return 0;
|
||||
var lzrc = new Lzrc();
|
||||
lzrc.lzrc_decompress(@out, size, @in, insize);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,836 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class Lzrc
|
||||
{
|
||||
private byte[] input;
|
||||
private int in_ptr;
|
||||
private int in_len;
|
||||
|
||||
private byte[] output;
|
||||
private int out_ptr;
|
||||
private int out_len;
|
||||
|
||||
private uint range;
|
||||
private uint code;
|
||||
private uint out_code;
|
||||
private byte lc;
|
||||
private byte[][] bm_literal = new byte[8][];
|
||||
private byte[][] bm_dist_bits = new byte[8][];
|
||||
private byte[][] bm_dist = new byte[16][];
|
||||
private byte[][] bm_match = new byte[8][];
|
||||
private byte[][] bm_len = new byte[8][];
|
||||
|
||||
const int max_tbl_sz = 65280;
|
||||
const int tbl_sz = 65536;
|
||||
|
||||
static byte[] text_buf = new byte[tbl_sz];
|
||||
static int t_start, t_end, t_fill, sp_fill;
|
||||
static int t_len, t_pos;
|
||||
|
||||
static int[] prev = new int[tbl_sz], next = new int[tbl_sz];
|
||||
static int[] root = new int[tbl_sz];
|
||||
|
||||
static void Init(byte[][] arr, byte value, int length)
|
||||
{
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
{
|
||||
arr[i] = new byte[length];
|
||||
for (int j = 0; j < length; j++)
|
||||
{
|
||||
arr[i][j] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte rc_getbyte()
|
||||
{
|
||||
if (in_ptr == in_len)
|
||||
{
|
||||
throw new Exception("End of input!");
|
||||
}
|
||||
|
||||
return input[in_ptr++];
|
||||
}
|
||||
|
||||
void rc_putbyte(byte b)
|
||||
{
|
||||
if (out_ptr == out_len)
|
||||
{
|
||||
throw new Exception("Output overflow!");
|
||||
}
|
||||
|
||||
output[out_ptr++] = b;
|
||||
}
|
||||
|
||||
void re_init(ref byte[] out_buf, int out_len, ref byte[] in_buf, int in_len)
|
||||
{
|
||||
input = in_buf;
|
||||
this.in_len = in_len;
|
||||
in_ptr = 0;
|
||||
|
||||
output = out_buf;
|
||||
this.out_len = out_len;
|
||||
out_ptr = 0;
|
||||
|
||||
range = 0xffffffff;
|
||||
code = 0x00000000;
|
||||
lc = 5;
|
||||
out_code = 0xffffffff;
|
||||
|
||||
re_putbyte(lc);
|
||||
|
||||
|
||||
#if NP9660
|
||||
Init(bm_literal, 0x80, 2048);
|
||||
Init(bm_dist_bits, 0x80, 312);
|
||||
Init(bm_dist, 0x80, 144);
|
||||
Init(bm_match, 0x80, 64);
|
||||
Init(bm_len, 0x80, 248);
|
||||
#else
|
||||
Init(bm_literal, 0x80, 256); // 2048 2680 2656
|
||||
Init(bm_dist_bits, 0x80, 23); // 184
|
||||
Init(bm_dist, 0x80, 8); // 128
|
||||
Init(bm_match, 0x80, 8); // 64
|
||||
Init(bm_len, 0x80, 32); // 256
|
||||
#endif
|
||||
//memset(re->bm_literal, 0x80, 2048);
|
||||
//memset(re->bm_dist_bits, 0x80, 312);
|
||||
//memset(re->bm_dist, 0x80, 144);
|
||||
//memset(re->bm_match, 0x80, 64);
|
||||
//memset(re->bm_len, 0x80, 248);
|
||||
}
|
||||
|
||||
void rc_init(byte[] out_buf, int out_len, byte[] in_buf, int in_len)
|
||||
{
|
||||
input = in_buf;
|
||||
this.in_len = in_len;
|
||||
in_ptr = 0;
|
||||
|
||||
output = out_buf;
|
||||
this.out_len = out_len;
|
||||
out_ptr = 0;
|
||||
|
||||
range = 0xffffffff;
|
||||
lc = rc_getbyte();
|
||||
code = (uint)((rc_getbyte() << 24) |
|
||||
(rc_getbyte() << 16) |
|
||||
(rc_getbyte() << 8) |
|
||||
rc_getbyte());
|
||||
out_code = 0xffffffff;
|
||||
|
||||
Init(bm_literal, 0x80, 256); // 2048 2680 2656
|
||||
Init(bm_dist_bits, 0x80, 23); // 184
|
||||
Init(bm_dist, 0x80, 8); // 128
|
||||
Init(bm_match, 0x80, 8); // 64
|
||||
Init(bm_len, 0x80, 32); // 256
|
||||
}
|
||||
|
||||
void normalize()
|
||||
{
|
||||
if (range < 0x01000000)
|
||||
{
|
||||
range <<= 8;
|
||||
code = (code << 8) + input[in_ptr];
|
||||
in_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
int rc_bit(byte[] probs, int index)
|
||||
{
|
||||
uint bound;
|
||||
|
||||
normalize();
|
||||
|
||||
bound = (range >> 8) * probs[index];
|
||||
probs[index] -= (byte)(probs[index] >> 3);
|
||||
|
||||
if (code < bound)
|
||||
{
|
||||
range = bound;
|
||||
probs[index] += 31;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
code -= bound;
|
||||
range -= bound;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int rc_bittree(byte[] probs, int index, int limit)
|
||||
{
|
||||
int number = 1;
|
||||
do
|
||||
{
|
||||
number = (number << 1) + rc_bit(probs, index + number);
|
||||
} while (number < limit);
|
||||
|
||||
number -= limit;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
int rc_number(byte[] prob, int index, int n)
|
||||
{
|
||||
int i, number = 1;
|
||||
|
||||
if (n > 3)
|
||||
{
|
||||
number = (number << 1) + rc_bit(prob, index + 3);
|
||||
if (n > 4)
|
||||
{
|
||||
number = (number << 1) + rc_bit(prob, index + 3);
|
||||
}
|
||||
|
||||
if (n > 5)
|
||||
{
|
||||
normalize();
|
||||
for (i = 0; i < n - 5; i++)
|
||||
{
|
||||
range >>= 1;
|
||||
number <<= 1;
|
||||
if (code < range)
|
||||
{
|
||||
number += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
code -= range;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
number = (number << 1) + rc_bit(prob, index + 0);
|
||||
if (n > 1)
|
||||
{
|
||||
number = (number << 1) + rc_bit(prob, index + 1);
|
||||
if (n > 2)
|
||||
{
|
||||
number = (number << 1) + rc_bit(prob, index + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
public void init_tree()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tbl_sz; i++)
|
||||
{
|
||||
root[i] = -1;
|
||||
prev[i] = -1;
|
||||
next[i] = -1;
|
||||
}
|
||||
|
||||
t_start = 0;
|
||||
t_end = 0;
|
||||
t_fill = 0;
|
||||
sp_fill = 0;
|
||||
}
|
||||
|
||||
void fill_buffer()
|
||||
{
|
||||
//void *memcpy(void *dest, const void * src, size_t n)
|
||||
|
||||
int content_size, back_size, front_size;
|
||||
|
||||
if (sp_fill == in_len)
|
||||
return;
|
||||
|
||||
content_size = (t_fill < t_end) ? (max_tbl_sz + t_fill - t_end) : (t_fill - t_end);
|
||||
if (content_size >= 509)
|
||||
return;
|
||||
|
||||
if (t_fill < t_start)
|
||||
{
|
||||
back_size = t_start - t_fill - 1;
|
||||
if (sp_fill + back_size > in_len)
|
||||
back_size = in_len - sp_fill;
|
||||
|
||||
Array.ConstrainedCopy(input, sp_fill, text_buf, t_fill, back_size);
|
||||
// memcpy(text_buf + t_fill, re->input + sp_fill, back_size);
|
||||
|
||||
sp_fill += back_size;
|
||||
t_fill += back_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
back_size = max_tbl_sz - t_fill;
|
||||
if (t_start == 0)
|
||||
back_size -= 1;
|
||||
if (sp_fill + back_size > in_len)
|
||||
back_size = in_len - sp_fill;
|
||||
|
||||
Array.ConstrainedCopy(input, sp_fill, text_buf, t_fill, back_size);
|
||||
//memcpy(text_buf + t_fill, re->input + sp_fill, back_size);
|
||||
|
||||
sp_fill += back_size;
|
||||
t_fill += back_size;
|
||||
|
||||
front_size = t_start;
|
||||
if (t_start != 0)
|
||||
front_size -= 1;
|
||||
if (sp_fill + front_size > in_len)
|
||||
front_size = in_len - sp_fill;
|
||||
|
||||
Array.ConstrainedCopy(input, sp_fill, text_buf, 0, front_size);
|
||||
//memcpy(text_buf, re->input + sp_fill, front_size);
|
||||
|
||||
sp_fill += front_size;
|
||||
|
||||
Array.ConstrainedCopy(text_buf, 255, text_buf, max_tbl_sz, front_size);
|
||||
//memcpy(text_buf + max_tbl_sz, text_buf, 255);
|
||||
|
||||
t_fill += front_size;
|
||||
if (t_fill >= max_tbl_sz)
|
||||
t_fill -= max_tbl_sz;
|
||||
}
|
||||
}
|
||||
void remove_node(int p)
|
||||
{
|
||||
int t, q;
|
||||
|
||||
if (prev[p] == -1)
|
||||
return;
|
||||
|
||||
t = text_buf[p + 0];
|
||||
t = (t << 8) | text_buf[p + 1];
|
||||
|
||||
q = next[p];
|
||||
if (q != -1)
|
||||
prev[q] = prev[p];
|
||||
|
||||
if (prev[p] == -2)
|
||||
root[t] = q;
|
||||
else
|
||||
next[prev[p]] = q;
|
||||
|
||||
prev[p] = -1;
|
||||
next[p] = -1;
|
||||
}
|
||||
|
||||
int insert_node(int pos, out int match_len, out int match_dist, int do_cmp)
|
||||
{
|
||||
//Span<byte> src, win;
|
||||
int i, t, p;
|
||||
int content_size;
|
||||
|
||||
//src = text_buf[pos..];
|
||||
//win = text_buf[t_start..];
|
||||
content_size = (t_fill < pos) ? (max_tbl_sz + t_fill - pos) : (t_fill - pos);
|
||||
t_len = 1;
|
||||
t_pos = 0;
|
||||
match_len = t_len;
|
||||
match_dist = t_pos;
|
||||
|
||||
if (in_ptr == in_len)
|
||||
{
|
||||
match_len = 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (in_ptr == (in_len - 1))
|
||||
return 0;
|
||||
|
||||
t = text_buf[pos+0];
|
||||
t = (t << 8) | text_buf[pos+1];
|
||||
if (root[t] == -1)
|
||||
{
|
||||
root[t] = pos;
|
||||
prev[pos] = -2;
|
||||
next[pos] = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = root[t];
|
||||
root[t] = pos;
|
||||
prev[pos] = -2;
|
||||
next[pos] = p;
|
||||
|
||||
if (p != -1)
|
||||
prev[p] = pos;
|
||||
|
||||
while (do_cmp == 1 && p != -1)
|
||||
{
|
||||
for (i = 0; (i < 255 && i < content_size); i++)
|
||||
{
|
||||
if (text_buf[pos+i] != text_buf[p + i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > t_len)
|
||||
{
|
||||
t_len = i;
|
||||
t_pos = pos - p;
|
||||
}
|
||||
else if (i == t_len)
|
||||
{
|
||||
int mp = pos - p;
|
||||
if (mp < 0)
|
||||
mp += max_tbl_sz;
|
||||
if (mp < t_pos)
|
||||
{
|
||||
t_len = i;
|
||||
t_pos = pos - p;
|
||||
}
|
||||
}
|
||||
if (i == 255)
|
||||
{
|
||||
remove_node(p);
|
||||
break;
|
||||
}
|
||||
|
||||
p = next[p];
|
||||
}
|
||||
|
||||
match_len = t_len;
|
||||
match_dist = t_pos;
|
||||
|
||||
return 1;
|
||||
}
|
||||
void update_tree(int length)
|
||||
{
|
||||
int i, win_size;
|
||||
int tmp_len, tmp_pos;
|
||||
|
||||
win_size = (t_end >= t_start) ? (t_end - t_start) : (max_tbl_sz + t_end - t_start);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
if (win_size == 16384)
|
||||
{
|
||||
remove_node(t_start);
|
||||
t_start += 1;
|
||||
if (t_start == max_tbl_sz)
|
||||
t_start = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
win_size += 1;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
insert_node(t_end, out tmp_len, out tmp_pos, 0);
|
||||
}
|
||||
t_end += 1;
|
||||
if (t_end >= max_tbl_sz)
|
||||
t_end -= max_tbl_sz;
|
||||
}
|
||||
}
|
||||
void re_bittree(ref byte[] probs,int index, int limit, int number)
|
||||
{
|
||||
int n, tmp, bit;
|
||||
|
||||
number += limit;
|
||||
|
||||
// Get total bits used by number
|
||||
tmp = number;
|
||||
n = 0;
|
||||
while (tmp > 1)
|
||||
{
|
||||
tmp >>= 1;
|
||||
n++;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
tmp = number >> n;
|
||||
bit = (number >> (n - 1)) & 1;
|
||||
re_bit(ref probs, index + tmp, bit);
|
||||
|
||||
n -= 1;
|
||||
} while (n > 0);
|
||||
//number = (res_number - limit);
|
||||
}
|
||||
|
||||
void re_bit(ref byte[] prob, int index, int bit)
|
||||
{
|
||||
uint bound;
|
||||
uint old_r, old_c;
|
||||
byte old_p;
|
||||
|
||||
re_normalize();
|
||||
|
||||
old_r = range;
|
||||
old_c = code;
|
||||
old_p = prob[index];
|
||||
|
||||
var pProb = prob[index];
|
||||
|
||||
bound = (range >> 8) * (pProb);
|
||||
pProb -= (byte)(pProb >> 3);
|
||||
|
||||
if (bit != 0)
|
||||
{
|
||||
range = bound;
|
||||
pProb += 31;
|
||||
}
|
||||
else
|
||||
{
|
||||
code += bound;
|
||||
if (code < old_c)
|
||||
out_code += 1;
|
||||
range -= bound;
|
||||
}
|
||||
|
||||
prob[index] = pProb;
|
||||
}
|
||||
|
||||
void re_normalize()
|
||||
{
|
||||
if (range < 0x01000000)
|
||||
{
|
||||
if (out_code != 0xffffffff)
|
||||
{
|
||||
if (out_code > 255)
|
||||
{
|
||||
int p, old_c;
|
||||
p = out_ptr - 1;
|
||||
do
|
||||
{
|
||||
old_c = output[p];
|
||||
output[p] += 1;
|
||||
p -= 1;
|
||||
} while (old_c == 0xff);
|
||||
}
|
||||
|
||||
re_putbyte((byte)(out_code & 0xff));
|
||||
}
|
||||
out_code = (code >> 24) & 0xff;
|
||||
range <<= 8;
|
||||
code <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
void re_putbyte(byte out_byte)
|
||||
{
|
||||
if (out_ptr == out_len)
|
||||
{
|
||||
throw new Exception("Output overflow!");
|
||||
}
|
||||
|
||||
output[out_ptr++] = out_byte;
|
||||
}
|
||||
byte re_getbyte()
|
||||
{
|
||||
if (in_ptr == in_len)
|
||||
{
|
||||
throw new Exception("End of input!");
|
||||
}
|
||||
|
||||
return input[in_ptr++];
|
||||
}
|
||||
|
||||
void re_number(ref byte[] prob, int index, int n, int number)
|
||||
{
|
||||
int i;
|
||||
UInt32 old_c;
|
||||
|
||||
i = 1;
|
||||
|
||||
if (n > 3)
|
||||
{
|
||||
re_bit(ref prob, index + 3,(number >> (n - i)) & 1);
|
||||
i += 1;
|
||||
if (n > 4)
|
||||
{
|
||||
re_bit(ref prob, index + 3, (number >> (n - i)) & 1);
|
||||
i += 1;
|
||||
if (n > 5)
|
||||
{
|
||||
re_normalize();
|
||||
for (i = 3; i < n - 2; i++)
|
||||
{
|
||||
range >>= 1;
|
||||
if (((number >> (n - i)) & 1) == 0)
|
||||
{
|
||||
old_c = code;
|
||||
code += range;
|
||||
if (code < old_c)
|
||||
out_code += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
re_bit(ref prob, index + 0, (number >> (n - i - 0)) & 1);
|
||||
if (n > 1)
|
||||
{
|
||||
re_bit(ref prob, index + 1, (number >> (n - i - 1)) & 1);
|
||||
if (n > 2)
|
||||
{
|
||||
re_bit(ref prob, index + 2, (number >> (n - i - 2)) & 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void re_flush()
|
||||
{
|
||||
re_putbyte((byte)((out_code) & 0xff));
|
||||
re_putbyte((byte)((code >> 24) & 0xff));
|
||||
re_putbyte((byte)((code >> 16) & 0xff));
|
||||
re_putbyte((byte)((code >> 8) & 0xff));
|
||||
re_putbyte((byte)((code >> 0) & 0xff));
|
||||
}
|
||||
public int lzrc_compress(byte[] out_buf, int out_len, byte[] in_buf, int in_len)
|
||||
{
|
||||
int match_step, re_state, len_state, dist_state;
|
||||
int i, cur_byte, last_byte;
|
||||
int match_len, len_bits;
|
||||
int match_dist, dist_bits, limit;
|
||||
int round = -1;
|
||||
|
||||
len_state = 0;
|
||||
|
||||
// initalize buffers to all 0x80
|
||||
re_init(ref out_buf, out_len, ref in_buf, in_len);
|
||||
|
||||
// initalize the tree
|
||||
init_tree();
|
||||
|
||||
// initalize variable to 0 ...
|
||||
re_state = 0;
|
||||
last_byte = 0;
|
||||
match_len = 0;
|
||||
match_dist = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
round += 1;
|
||||
match_step = 0;
|
||||
|
||||
fill_buffer();
|
||||
insert_node(t_end, out match_len, out match_dist, 1);
|
||||
|
||||
if (match_len < 256)
|
||||
{
|
||||
#if NP9660
|
||||
if (match_len < 4 && match_dist > 255)
|
||||
#else
|
||||
if (match_len <= 4 && match_dist > 255)
|
||||
#endif
|
||||
match_len = 1;
|
||||
update_tree(match_len);
|
||||
}
|
||||
#if NP9660
|
||||
if ((match_len == 1 || (match_len < 4 && match_dist > 255)))
|
||||
#else
|
||||
if ((match_len == 1 || (match_len <= 4 && match_dist > 255)))
|
||||
#endif
|
||||
{
|
||||
re_bit(ref bm_match[re_state], match_step, 0);
|
||||
|
||||
if (re_state > 0)
|
||||
re_state -= 1;
|
||||
|
||||
cur_byte = re_getbyte();
|
||||
re_bittree(ref bm_literal[((last_byte >> lc) & 0x07)], 0, 0x100, cur_byte);
|
||||
|
||||
if (in_ptr == in_len)
|
||||
{
|
||||
re_normalize();
|
||||
re_flush();
|
||||
return out_ptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
re_bit(ref bm_match[re_state], match_step, 1);
|
||||
|
||||
// write bitstream length (8 bits, where 0 marks the end of it)
|
||||
len_bits = 0;
|
||||
for (i = 1; i < 8; i++)
|
||||
{
|
||||
match_step += 1;
|
||||
if ((match_len - 1) < (1 << i))
|
||||
break;
|
||||
re_bit(ref bm_match[re_state], match_step, 1);
|
||||
len_bits += 1;
|
||||
}
|
||||
if (i != 8)
|
||||
{
|
||||
re_bit(ref bm_match[re_state], match_step, 0);
|
||||
}
|
||||
|
||||
if (len_bits > 0)
|
||||
{
|
||||
len_state = ((len_bits - 1) << 2) + ((in_ptr << (len_bits - 1)) & 0x03);
|
||||
re_number(ref bm_len[re_state], len_state, len_bits, (match_len - 1));
|
||||
|
||||
if (in_ptr == in_len)
|
||||
{
|
||||
re_normalize();
|
||||
re_flush();
|
||||
return out_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// determine limit ...
|
||||
dist_state = 0;
|
||||
limit = 8;
|
||||
#if NP9660
|
||||
if ( match_len > 3)
|
||||
#else
|
||||
if( (match_len) > 4 )
|
||||
#endif
|
||||
{
|
||||
dist_state += 7;
|
||||
#if NP9660
|
||||
limit = 44;
|
||||
#else
|
||||
limit = 16;
|
||||
#endif
|
||||
}
|
||||
|
||||
// find total 1s in the match_dist
|
||||
dist_bits = 0;
|
||||
if(match_dist != 0) {
|
||||
while ((match_dist >> dist_bits) != 1)
|
||||
dist_bits += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Match dist is 0.. uhh cant match with yourself..");
|
||||
}
|
||||
|
||||
re_bittree(ref bm_dist_bits[len_bits], dist_state, limit, dist_bits);
|
||||
|
||||
if (dist_bits > 0)
|
||||
{
|
||||
re_number(ref bm_dist[dist_bits], 0, dist_bits, match_dist);
|
||||
}
|
||||
|
||||
|
||||
in_ptr += match_len;
|
||||
re_state = 6 + ((in_ptr + 1) & 1);
|
||||
}
|
||||
last_byte = input[in_ptr - 1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int lzrc_decompress(byte[] out_buf, int out_len, byte[] in_buf, int in_len)
|
||||
{
|
||||
int match_step, rc_state, len_state, dist_state = 0;
|
||||
int i, bit, cur_byte, last_byte;
|
||||
int match_len, len_bits;
|
||||
int match_dist, dist_bits, limit;
|
||||
int match_src;
|
||||
int round = -1;
|
||||
|
||||
len_state = 0;
|
||||
|
||||
rc_init(out_buf, out_len, in_buf, in_len);
|
||||
|
||||
if ((lc & 0x80) != 0)
|
||||
{
|
||||
Buffer.BlockCopy(in_buf, 5, out_buf, 0, (int)code);
|
||||
return (int)code;
|
||||
}
|
||||
|
||||
rc_state = 0;
|
||||
last_byte = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
round += 1;
|
||||
match_step = 0;
|
||||
bit = rc_bit(bm_match[rc_state], match_step);
|
||||
// if bit is 0, just copy from the bit tree into the output ?
|
||||
if (bit == 0)
|
||||
{
|
||||
if (rc_state > 0)
|
||||
{
|
||||
rc_state -= 1;
|
||||
}
|
||||
|
||||
cur_byte = rc_bittree(bm_literal[(((out_ptr & 7) << 8) + last_byte >> lc) & 0x07], 0, 0x100);
|
||||
|
||||
rc_putbyte((byte)cur_byte);
|
||||
if (out_ptr == out_len) return out_ptr;
|
||||
}
|
||||
else // This essentially goes; "hey the bytes that go here, are the same ones that were already *here*
|
||||
{
|
||||
// Determine bit length?
|
||||
len_bits = 0;
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
match_step += 1;
|
||||
bit = rc_bit(bm_match[rc_state], match_step);
|
||||
if (bit == 0)
|
||||
break;
|
||||
len_bits += 1;
|
||||
}
|
||||
|
||||
// Get the match length ..
|
||||
if (len_bits == 0)
|
||||
{
|
||||
match_len = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len_state = ((len_bits - 1) << 2) + ((out_ptr << (len_bits - 1)) & 0x03);
|
||||
match_len = rc_number(bm_len[rc_state], len_state, len_bits);
|
||||
//if ((match_len == 0xFF || match_len == 0xFE) && out_ptr == out_len)
|
||||
//{
|
||||
// return out_ptr;
|
||||
//}
|
||||
}
|
||||
|
||||
dist_state = 0;
|
||||
limit = 8;
|
||||
if (match_len > 3)
|
||||
{
|
||||
dist_state += 7;
|
||||
limit = 16;
|
||||
}
|
||||
dist_bits = rc_bittree(bm_dist_bits[len_bits], dist_state, limit);
|
||||
|
||||
if (dist_bits > 0)
|
||||
{
|
||||
match_dist = rc_number(bm_dist[dist_bits], 0, dist_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
match_dist = 1;
|
||||
}
|
||||
|
||||
match_src = out_ptr - match_dist;
|
||||
if (match_dist > out_ptr || match_dist < 0)
|
||||
{
|
||||
//Console.WriteLine($"match_dist out of range! 0x{match_dist:x8}");
|
||||
//return -1; // test
|
||||
throw new Exception($"match_dist out of range! 0x{match_dist:x8}");
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < match_len + 1; i++)
|
||||
{
|
||||
rc_putbyte(output[match_src++]);
|
||||
}
|
||||
rc_state = 6 + ((out_ptr + 1) & 1);
|
||||
if (out_ptr == out_len) return out_ptr;
|
||||
}
|
||||
last_byte = output[out_ptr - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.1.1" />
|
||||
<PackageReference Include="DotNetZip" Version="1.16.0" />
|
||||
<PackageReference Include="LZMA-SDK" Version="19.0.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resource.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ECDsaTest
|
||||
{
|
||||
internal class PspParameter
|
||||
{
|
||||
public byte[] Kinv { get; set; }
|
||||
public byte[] Rp { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PspCrypto {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resource {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PspCrypto.Resource", typeof(Resource).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] @__sce_discinfo {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("__sce_discinfo", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="__sce_discinfo" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>__sce_discinfo;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class RijndaelMod : Aes
|
||||
{
|
||||
public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV)
|
||||
{
|
||||
return CreateTransform(rgbKey, encrypting: false);
|
||||
}
|
||||
|
||||
public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)
|
||||
{
|
||||
return CreateTransform(rgbKey, encrypting: true);
|
||||
}
|
||||
|
||||
public override void GenerateIV()
|
||||
{
|
||||
}
|
||||
|
||||
public override void GenerateKey()
|
||||
{
|
||||
byte[] key = new byte[KeySize / BitsPerByte];
|
||||
RandomNumberGenerator.Fill(key);
|
||||
Key = key;
|
||||
}
|
||||
|
||||
private ICryptoTransform CreateTransform(byte[] rgbKey, bool encrypting)
|
||||
{
|
||||
if (rgbKey == null)
|
||||
throw new ArgumentNullException(nameof(rgbKey));
|
||||
return new RijndaelModTransform(rgbKey, BlockSizeValue, encrypting);
|
||||
}
|
||||
|
||||
private const int BitsPerByte = 8;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,272 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using PspCrypto.Security.Cryptography;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class SHA224Managed_OLD : SHA224
|
||||
{
|
||||
|
||||
private const int BLOCK_SIZE_BYTES = 64;
|
||||
|
||||
private uint[] _H;
|
||||
private ulong count;
|
||||
private byte[] _ProcessingBuffer; // Used to start data when passed less than a block worth.
|
||||
private int _ProcessingBufferCount; // Counts how much data we have stored that still needs processed.
|
||||
private uint[] buff;
|
||||
|
||||
public SHA224Managed_OLD()
|
||||
{
|
||||
_H = new uint[8];
|
||||
_ProcessingBuffer = new byte[BLOCK_SIZE_BYTES];
|
||||
buff = new uint[64];
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private uint Ch(uint u, uint v, uint w)
|
||||
{
|
||||
return (u & v) ^ (~u & w);
|
||||
}
|
||||
|
||||
private uint Maj(uint u, uint v, uint w)
|
||||
{
|
||||
return (u & v) ^ (u & w) ^ (v & w);
|
||||
}
|
||||
|
||||
private uint Ro0(uint x)
|
||||
{
|
||||
return ((x >> 7) | (x << 25))
|
||||
^ ((x >> 18) | (x << 14))
|
||||
^ (x >> 3);
|
||||
}
|
||||
|
||||
private uint Ro1(uint x)
|
||||
{
|
||||
return ((x >> 17) | (x << 15))
|
||||
^ ((x >> 19) | (x << 13))
|
||||
^ (x >> 10);
|
||||
}
|
||||
|
||||
private uint Sig0(uint x)
|
||||
{
|
||||
return ((x >> 2) | (x << 30))
|
||||
^ ((x >> 13) | (x << 19))
|
||||
^ ((x >> 22) | (x << 10));
|
||||
}
|
||||
|
||||
private uint Sig1(uint x)
|
||||
{
|
||||
return ((x >> 6) | (x << 26))
|
||||
^ ((x >> 11) | (x << 21))
|
||||
^ ((x >> 25) | (x << 7));
|
||||
}
|
||||
|
||||
|
||||
public void HashData2(ReadOnlySpan<byte> data, Span<byte> hash)
|
||||
{
|
||||
var tmp = ComputeHash(data.ToArray());
|
||||
tmp.CopyTo(hash);
|
||||
}
|
||||
|
||||
protected override void HashCore(byte[] rgb, int start, int size)
|
||||
{
|
||||
int i;
|
||||
State = 1;
|
||||
|
||||
if (_ProcessingBufferCount != 0)
|
||||
{
|
||||
if (size < (BLOCK_SIZE_BYTES - _ProcessingBufferCount))
|
||||
{
|
||||
System.Buffer.BlockCopy(rgb, start, _ProcessingBuffer, _ProcessingBufferCount, size);
|
||||
_ProcessingBufferCount += size;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = (BLOCK_SIZE_BYTES - _ProcessingBufferCount);
|
||||
System.Buffer.BlockCopy(rgb, start, _ProcessingBuffer, _ProcessingBufferCount, i);
|
||||
ProcessBlock(_ProcessingBuffer, 0);
|
||||
_ProcessingBufferCount = 0;
|
||||
start += i;
|
||||
size -= i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < size - size % BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES)
|
||||
{
|
||||
ProcessBlock(rgb, start + i);
|
||||
}
|
||||
|
||||
if (size % BLOCK_SIZE_BYTES != 0)
|
||||
{
|
||||
System.Buffer.BlockCopy(rgb, size - size % BLOCK_SIZE_BYTES + start, _ProcessingBuffer, 0, size % BLOCK_SIZE_BYTES);
|
||||
_ProcessingBufferCount = size % BLOCK_SIZE_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = new byte[28];
|
||||
int i, j;
|
||||
|
||||
ProcessFinalBlock(_ProcessingBuffer, 0, _ProcessingBufferCount);
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
hash[i * 4 + j] = (byte)(_H[i] >> (24 - j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
State = 0;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
count = 0;
|
||||
_ProcessingBufferCount = 0;
|
||||
|
||||
_H[0] = 0xC1059ED8;
|
||||
_H[1] = 0x367CD507;
|
||||
_H[2] = 0x3070DD17;
|
||||
_H[3] = 0xF70E5939;
|
||||
_H[4] = 0xFFC00B31;
|
||||
_H[5] = 0x68581511;
|
||||
_H[6] = 0x64F98FA7;
|
||||
_H[7] = 0xBEFA4FA4;
|
||||
}
|
||||
|
||||
private void ProcessBlock(byte[] inputBuffer, int inputOffset)
|
||||
{
|
||||
uint a, b, c, d, e, f, g, h;
|
||||
uint t1, t2;
|
||||
int i;
|
||||
uint[] K1 = _K1;
|
||||
uint[] buff = this.buff;
|
||||
|
||||
count += BLOCK_SIZE_BYTES;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
buff[i] = (uint)(((inputBuffer[inputOffset + 4 * i]) << 24)
|
||||
| ((inputBuffer[inputOffset + 4 * i + 1]) << 16)
|
||||
| ((inputBuffer[inputOffset + 4 * i + 2]) << 8)
|
||||
| ((inputBuffer[inputOffset + 4 * i + 3])));
|
||||
}
|
||||
|
||||
|
||||
for (i = 16; i < 64; i++)
|
||||
{
|
||||
t1 = buff[i - 15];
|
||||
t1 = (((t1 >> 7) | (t1 << 25)) ^ ((t1 >> 18) | (t1 << 14)) ^ (t1 >> 3));
|
||||
|
||||
t2 = buff[i - 2];
|
||||
t2 = (((t2 >> 17) | (t2 << 15)) ^ ((t2 >> 19) | (t2 << 13)) ^ (t2 >> 10));
|
||||
buff[i] = t2 + buff[i - 7] + t1 + buff[i - 16];
|
||||
}
|
||||
|
||||
a = _H[0];
|
||||
b = _H[1];
|
||||
c = _H[2];
|
||||
d = _H[3];
|
||||
e = _H[4];
|
||||
f = _H[5];
|
||||
g = _H[6];
|
||||
h = _H[7];
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
t1 = h + (((e >> 6) | (e << 26)) ^ ((e >> 11) | (e << 21)) ^ ((e >> 25) | (e << 7))) + ((e & f) ^ (~e & g)) + K1[i] + buff[i];
|
||||
|
||||
t2 = (((a >> 2) | (a << 30)) ^ ((a >> 13) | (a << 19)) ^ ((a >> 22) | (a << 10)));
|
||||
t2 = t2 + ((a & b) ^ (a & c) ^ (b & c));
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
_H[0] += a;
|
||||
_H[1] += b;
|
||||
_H[2] += c;
|
||||
_H[3] += d;
|
||||
_H[4] += e;
|
||||
_H[5] += f;
|
||||
_H[6] += g;
|
||||
_H[7] += h;
|
||||
}
|
||||
|
||||
private void ProcessFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
|
||||
{
|
||||
ulong total = count + (ulong)inputCount;
|
||||
int paddingSize = (56 - (int)(total % BLOCK_SIZE_BYTES));
|
||||
|
||||
if (paddingSize < 1)
|
||||
paddingSize += BLOCK_SIZE_BYTES;
|
||||
|
||||
byte[] fooBuffer = new byte[inputCount + paddingSize + 8];
|
||||
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
fooBuffer[i] = inputBuffer[i + inputOffset];
|
||||
}
|
||||
|
||||
fooBuffer[inputCount] = 0x80;
|
||||
for (int i = inputCount + 1; i < inputCount + paddingSize; i++)
|
||||
{
|
||||
fooBuffer[i] = 0x00;
|
||||
}
|
||||
|
||||
// I deal in bytes. The algorithm deals in bits.
|
||||
ulong size = total << 3;
|
||||
AddLength(size, fooBuffer, inputCount + paddingSize);
|
||||
ProcessBlock(fooBuffer, 0);
|
||||
|
||||
if (inputCount + paddingSize + 8 == 128)
|
||||
{
|
||||
ProcessBlock(fooBuffer, 64);
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddLength(ulong length, byte[] buffer, int position)
|
||||
{
|
||||
buffer[position++] = (byte)(length >> 56);
|
||||
buffer[position++] = (byte)(length >> 48);
|
||||
buffer[position++] = (byte)(length >> 40);
|
||||
buffer[position++] = (byte)(length >> 32);
|
||||
buffer[position++] = (byte)(length >> 24);
|
||||
buffer[position++] = (byte)(length >> 16);
|
||||
buffer[position++] = (byte)(length >> 8);
|
||||
buffer[position] = (byte)(length);
|
||||
}
|
||||
|
||||
// SHA-224/256 Constants
|
||||
// Represent the first 32 bits of the fractional parts of the
|
||||
// cube roots of the first sixty-four prime numbers
|
||||
public readonly static uint[] _K1 = {
|
||||
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
||||
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
||||
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
||||
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
||||
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
||||
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
||||
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
||||
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
||||
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
||||
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
||||
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
||||
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
||||
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
||||
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
||||
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
||||
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace ECDsaTest.SafeHandles
|
||||
{
|
||||
internal sealed class SafeBignumHandle : SafeHandle
|
||||
{
|
||||
private SafeBignumHandle() :
|
||||
base(IntPtr.Zero, ownsHandle: true)
|
||||
{
|
||||
}
|
||||
|
||||
internal SafeBignumHandle(IntPtr handle, bool ownsHandle)
|
||||
: base(handle, ownsHandle)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.Crypto.BigNumDestroy(handle);
|
||||
SetHandle(IntPtr.Zero);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get { return handle == IntPtr.Zero; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace ECDsaTest.SafeHandles
|
||||
{
|
||||
internal sealed class SafeEcKeyHandle : SafeHandle
|
||||
{
|
||||
private SafeEcKeyHandle() :
|
||||
base(IntPtr.Zero, ownsHandle: true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.Crypto.EcKeyDestroy(handle);
|
||||
SetHandle(IntPtr.Zero);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get { return handle == IntPtr.Zero; }
|
||||
}
|
||||
|
||||
internal static SafeEcKeyHandle DuplicateHandle(IntPtr handle)
|
||||
{
|
||||
Debug.Assert(handle != IntPtr.Zero);
|
||||
|
||||
// Reliability: Allocate the SafeHandle before calling EC_KEY_up_ref so
|
||||
// that we don't lose a tracked reference in low-memory situations.
|
||||
SafeEcKeyHandle safeHandle = new SafeEcKeyHandle();
|
||||
|
||||
if (!Interop.Crypto.EcKeyUpRef(handle))
|
||||
{
|
||||
throw Interop.Crypto.CreateOpenSslCryptographicException();
|
||||
}
|
||||
|
||||
safeHandle.SetHandle(handle);
|
||||
return safeHandle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public static class SceDdrdb
|
||||
{
|
||||
public static int sceDdrdbHash(ReadOnlySpan<byte> src, int size, Span<byte> digest)
|
||||
{
|
||||
SHA1.HashData(src[..size], digest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int sceDdrdbSigvry(ReadOnlySpan<byte> pubKey, ReadOnlySpan<byte> hash, ReadOnlySpan<byte> sig)
|
||||
{
|
||||
Span<byte> buff = stackalloc byte[Marshal.SizeOf<KIRKEngine.KIRK_CMD17_BUFFER>()];
|
||||
ref KIRKEngine.KIRK_CMD17_BUFFER buffer = ref MemoryMarshal.AsRef<KIRKEngine.KIRK_CMD17_BUFFER>(buff);
|
||||
pubKey[..0x28].CopyTo(buffer.public_key.point);
|
||||
hash[..20].CopyTo(buffer.message_hash);
|
||||
sig[..0x28].CopyTo(buffer.signature.sig);
|
||||
return KIRKEngine.sceUtilsBufferCopyWithRange(null, 0, buff, 100, KIRKEngine.KIRK_CMD_ECDSA_VERIFY);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,513 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public static class SceMemlmd
|
||||
{
|
||||
private static readonly Memory<byte> MemlmdKirkMemory = new byte[0x150]; // DAT_00002604
|
||||
private static readonly Memory<byte> MemlmdMem_BFC00000 = new byte[0x3000];
|
||||
private static readonly Memory<byte> MemlmdMem_BFC00280 = MemlmdMem_BFC00000.Slice(0x280, 0xC0);
|
||||
private static readonly Memory<byte> MemlmdMem_BFC00340 = MemlmdMem_BFC00000.Slice(0x340, 0x300);
|
||||
private static readonly Memory<byte> MemlmdMem_BFC00A00 = MemlmdMem_BFC00000.Slice(0xA00, 0x200);
|
||||
private static readonly Memory<byte> MemlmdMem_2780 = new byte[0x200];
|
||||
|
||||
private static readonly byte[] key7A90 =
|
||||
{
|
||||
0x77, 0x3F, 0x4B, 0xE1, 0x4C, 0x0A, 0xB4, 0x52, 0x67, 0x2B, 0x67, 0x56, 0x82, 0x4C, 0xCF, 0x42,
|
||||
0xAA, 0x37, 0xFF, 0xC0, 0x89, 0x41, 0xE5, 0x63, 0x5E, 0x84, 0xE9, 0xFB, 0x53, 0xDA, 0x94, 0x9E,
|
||||
0x9B, 0xB7, 0xC2, 0xA4, 0x22, 0x9F, 0xDF, 0x1F
|
||||
};
|
||||
internal static readonly byte[] key_4C940AF0 = { 0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B };
|
||||
internal static readonly byte[] key_4C940BF0 = { 0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED, 0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A };
|
||||
internal static readonly byte[] key_4C9410F0 = { 0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34 };
|
||||
internal static readonly byte[] key_4C9412F0 = { 0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB };
|
||||
internal static readonly byte[] key_4C9413F0 = { 0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA };
|
||||
internal static readonly byte[] key_4C9414F0 = { 0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D };
|
||||
internal static readonly byte[] key_4C9415F0 = { 0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9 };
|
||||
internal static readonly byte[] key_4C949AF0 = { 0x48, 0x58, 0xAA, 0x38, 0x78, 0x9A, 0x6C, 0x0D, 0x42, 0xEA, 0xC8, 0x19, 0x23, 0x34, 0x4D, 0xF0 };
|
||||
internal static readonly byte[] key_4C949BF0 = { 0x20, 0x00, 0x5B, 0x67, 0x48, 0x77, 0x02, 0x60, 0xCF, 0x0C, 0xAB, 0x7E, 0xAE, 0x0C, 0x55, 0xA1 };
|
||||
internal static readonly byte[] key_4C949CF0 = { 0x3F, 0x67, 0x09, 0xA1, 0x47, 0x71, 0xD6, 0x9E, 0x27, 0x7C, 0x7B, 0x32, 0x67, 0x0E, 0x65, 0x8A };
|
||||
internal static readonly byte[] key_4C949DF0 = { 0x9B, 0x92, 0x99, 0x91, 0xA2, 0xE8, 0xAA, 0x4A, 0x87, 0x10, 0xA0, 0x9A, 0xBF, 0x88, 0xC0, 0xAC };
|
||||
internal static readonly byte[] key_4C949EF0 = { 0x90, 0x22, 0x66, 0xE9, 0x59, 0x11, 0x9B, 0x99, 0x67, 0x39, 0x49, 0x81, 0xAB, 0x98, 0x08, 0xA6 };
|
||||
internal static readonly byte[] key_4C949FF0 = { 0xA0, 0xA5, 0x55, 0x0A, 0xFA, 0xB2, 0x16, 0x62, 0x05, 0xDC, 0x4B, 0x8E, 0xDA, 0xD5, 0xA5, 0xCA };
|
||||
internal static readonly byte[] key_4C94A0F0 = { 0x78, 0x96, 0xAE, 0x9C, 0xE7, 0x89, 0x2D, 0xF5, 0x34, 0x9C, 0x29, 0x36, 0xD1, 0xF9, 0xE8, 0x3C };
|
||||
internal static readonly byte[] key_4C94A1F0 = { 0x71, 0x44, 0x53, 0xB6, 0xE6, 0x75, 0x3F, 0xF0, 0x8D, 0x5E, 0xB4, 0xB2, 0xEA, 0x06, 0x23, 0x6A };
|
||||
internal static readonly byte[] key_4C9491F0 = { 0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46 };
|
||||
internal static readonly byte[] key_4C9494F0 = { 0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9 };
|
||||
internal static readonly byte[] key_4C9490F0 = { 0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3 };
|
||||
|
||||
internal static readonly byte[] key_00000000 =
|
||||
{
|
||||
0x6A, 0x19, 0x71, 0xF3, 0x18, 0xDE, 0xD3, 0xA2, 0x6D, 0x3B, 0xDE, 0xC7, 0xBE, 0x98, 0xE2, 0x4C,
|
||||
0xE3, 0xDC, 0xDF, 0x42, 0x7B, 0x5B, 0x12, 0x28, 0x7D, 0xC0, 0x7A, 0x59, 0x86, 0xF0, 0xF5, 0xB5,
|
||||
0x58, 0xD8, 0x64, 0x18, 0x84, 0x24, 0x7F, 0xE9, 0x57, 0xAB, 0x4F, 0xC6, 0x92, 0x6D, 0x70, 0x29,
|
||||
0xD3, 0x61, 0x87, 0x87, 0xD0, 0xAE, 0x2C, 0xE7, 0x37, 0x77, 0xC7, 0x3C, 0x96, 0x7E, 0x21, 0x1F,
|
||||
0x65, 0x95, 0xC0, 0x61, 0x57, 0xAC, 0x64, 0xD8, 0x5A, 0x6D, 0x14, 0xD2, 0x9C, 0x54, 0xC6, 0x68,
|
||||
0x5D, 0xF5, 0xC3, 0xF0, 0x50, 0xDA, 0xEA, 0x19, 0x43, 0xA7, 0xAD, 0xC3, 0x2A, 0x14, 0xCA, 0xC8,
|
||||
0x4C, 0x83, 0x86, 0x18, 0xAE, 0x86, 0x49, 0xFB, 0x4F, 0x45, 0x75, 0xD2, 0xC3, 0xD6, 0xE1, 0x13,
|
||||
0x69, 0x37, 0xC6, 0x90, 0xCF, 0xF9, 0x79, 0xA1, 0x77, 0x3A, 0x3E, 0xBB, 0xBB, 0xD5, 0x3B, 0x84,
|
||||
0x1B, 0x9A, 0xB8, 0x79, 0xF0, 0xD3, 0x5F, 0x6F, 0x4C, 0xC0, 0x28, 0x87, 0xBC, 0xAE, 0xDA, 0x00
|
||||
};
|
||||
|
||||
internal static readonly byte[] key_01000000 =
|
||||
{
|
||||
0x50, 0xCC, 0x03, 0xAC, 0x3F, 0x53, 0x1A, 0xFA, 0x0A, 0xA4, 0x34, 0x23, 0x86, 0x61, 0x7F, 0x97,
|
||||
0x84, 0x1C, 0x1A, 0x1D, 0x08, 0xD4, 0x50, 0xB6, 0xD9, 0x73, 0x27, 0x80, 0xD1, 0xDE, 0xEE, 0xCA,
|
||||
0x49, 0x8B, 0x84, 0x37, 0xDB, 0xF0, 0x70, 0xA2, 0xA6, 0x2B, 0x09, 0x4D, 0x3B, 0x29, 0xDE, 0x0B,
|
||||
0xE1, 0x6F, 0x04, 0x7A, 0xC4, 0x18, 0x7A, 0x69, 0x73, 0xBF, 0x02, 0xD8, 0xA1, 0xD0, 0x58, 0x7E,
|
||||
0x69, 0xCE, 0xAC, 0x5E, 0x1B, 0x0A, 0xF8, 0x19, 0xE6, 0x9A, 0xC0, 0xDE, 0xA0, 0xB2, 0xCE, 0x04,
|
||||
0x43, 0xC0, 0x9D, 0x50, 0x5D, 0x0A, 0xD7, 0xFD, 0xC6, 0x53, 0xAA, 0x13, 0xDD, 0x2C, 0x3B, 0x2B,
|
||||
0xBF, 0xAB, 0x7C, 0xF5, 0xA0, 0x4A, 0x79, 0xE3, 0xF1, 0x7B, 0x2E, 0xB2, 0xA3, 0xAC, 0x8E, 0x0A,
|
||||
0x38, 0x9B, 0x9E, 0xAA, 0xEC, 0x2B, 0xA3, 0x75, 0x13, 0x75, 0x77, 0x98, 0x6A, 0x66, 0x92, 0x65,
|
||||
0xBC, 0x97, 0x80, 0x0E, 0x32, 0x88, 0x9F, 0x64, 0xBA, 0x99, 0x8A, 0x72, 0x96, 0x9F, 0xE1, 0xE0
|
||||
};
|
||||
|
||||
internal static readonly byte[] key_16D59E03 = { 0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E, 0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1 };
|
||||
|
||||
internal static readonly byte[] key_4467415D =
|
||||
{
|
||||
0x66, 0x0F, 0xCB, 0x3B, 0x30, 0x75, 0xE3, 0x10, 0x0A, 0x95, 0x65, 0xC7, 0x3C, 0x93, 0x87, 0x22,
|
||||
0xF3, 0xA4, 0xB1, 0xE8, 0x9A, 0xFB, 0x53, 0x52, 0x8F, 0x64, 0xB2, 0xDA, 0xB7, 0x76, 0xB9, 0x56,
|
||||
0x96, 0xB6, 0x4C, 0x02, 0xE6, 0x9B, 0xAE, 0xED, 0x86, 0x48, 0xBA, 0xA6, 0x4F, 0x23, 0x15, 0x03,
|
||||
0x1F, 0xC4, 0xF7, 0x3A, 0x05, 0xC3, 0x3C, 0xE2, 0x2F, 0x36, 0xC4, 0x26, 0xF2, 0x42, 0x40, 0x1F,
|
||||
0x97, 0xEE, 0x9C, 0xC6, 0xD9, 0x68, 0xE0, 0xE7, 0xE3, 0x9F, 0xCE, 0x05, 0xE8, 0xD1, 0x8B, 0x1B,
|
||||
0x57, 0x34, 0x3D, 0x0D, 0xDF, 0xA8, 0x64, 0xBF, 0x8F, 0x4C, 0x37, 0x3F, 0x93, 0xD5, 0x45, 0x9E,
|
||||
0x2B, 0x25, 0x2C, 0x62, 0x74, 0xDE, 0xC1, 0x53, 0xAB, 0x6D, 0xDF, 0x2C, 0xCE, 0x5A, 0x6B, 0x1F,
|
||||
0x5E, 0x24, 0x4A, 0xFB, 0x7D, 0xFF, 0xE8, 0xF5, 0x19, 0x77, 0xEF, 0xCC, 0x74, 0xFE, 0x8B, 0x63,
|
||||
0x31, 0xAE, 0x99, 0x05, 0x7F, 0x51, 0xF2, 0x72, 0x6A, 0x20, 0x8D, 0x1C, 0xAC, 0x4C, 0xF6, 0x50
|
||||
};
|
||||
|
||||
internal static readonly byte[] key_CFEF05F0 = { 0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40, 0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1 };
|
||||
internal static readonly byte[] key_CFEF06F0 = { 0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B, 0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77 };
|
||||
internal static readonly byte[] key_CFEF08F0 = { 0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A, 0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F };
|
||||
|
||||
public static int memlmd_EF73E85B(Span<byte> modData, int size, out int newSize) => KernelModuleDecrypt(modData, size, out newSize, 0);
|
||||
|
||||
public static int memlmd_CF03556B(Span<byte> modData, int size, out int newSize) => KernelModuleDecrypt(modData, size, out newSize, 1);
|
||||
|
||||
static int KernelModuleDecrypt(Span<byte> modData, int size, out int newSize, int use_polling)
|
||||
{
|
||||
int ret;
|
||||
newSize = 0;
|
||||
Span<byte> local_80 = stackalloc byte[48];
|
||||
Span<byte> local_50 = stackalloc byte[32];
|
||||
if (modData.IsEmpty)
|
||||
{
|
||||
return -0xc9;
|
||||
}
|
||||
if (size < 0x160)
|
||||
{
|
||||
return -0xca;
|
||||
}
|
||||
//if ((modData[0] & 0x3f) != 0)
|
||||
//{
|
||||
// return -0xcb;
|
||||
//}
|
||||
|
||||
//if ((0x220202 >> (MemoryMarshal.Read<int>(modData) >> 0x1b)) == 0)
|
||||
//{
|
||||
// return -0xcc;
|
||||
//}
|
||||
modData[..0x150].CopyTo(MemlmdKirkMemory.Span);
|
||||
var hdr = MemoryMarshal.Read<PSPHeader2>(MemlmdKirkMemory.Span);
|
||||
int? keySeed = null;
|
||||
bool? keyFlag = null;
|
||||
Span<byte> key = null;
|
||||
if (hdr.tag == 0x4C94A1F0)
|
||||
{
|
||||
keySeed = 0x43;
|
||||
keyFlag = true;
|
||||
key = key_4C94A1F0;
|
||||
}
|
||||
else if (hdr.tag == 0x4C949BF0)
|
||||
{
|
||||
keySeed = 0x43;
|
||||
keyFlag = true;
|
||||
key = key_4C949BF0;
|
||||
}
|
||||
else if (hdr.tag == 0xB1B9C434)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hdr.tag == 0x4C9491F0)
|
||||
{
|
||||
key = key_4C9491F0;
|
||||
}
|
||||
else if (hdr.tag == 0x4C9494F0)
|
||||
{
|
||||
key = key_4C9494F0;
|
||||
}
|
||||
else if (hdr.tag == 0x4C9490F0)
|
||||
{
|
||||
key = key_4C9490F0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hdr.tag == 0x00000000)
|
||||
{
|
||||
key = key_00000000;
|
||||
keySeed = 0x42;
|
||||
}
|
||||
else if (hdr.tag == 0x01000000)
|
||||
{
|
||||
key = key_01000000;
|
||||
keySeed = 0x43;
|
||||
}
|
||||
keyFlag = false;
|
||||
goto keytagout;
|
||||
}
|
||||
keySeed = 0x43;
|
||||
keyFlag = true;
|
||||
}
|
||||
keytagout:
|
||||
//if (keyFlag == true && size < 0x160)
|
||||
//{
|
||||
// return -0xca;
|
||||
//}
|
||||
if (keyFlag == true)
|
||||
{
|
||||
for (var i = 0; i < 0x30; i++)
|
||||
{
|
||||
if (hdr.sCheck[i] != 0)
|
||||
{
|
||||
ret = -0x12e;
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyFlag == false)
|
||||
{
|
||||
// TODO blacklistCheck
|
||||
}
|
||||
newSize = MemoryMarshal.Read<int>(hdr.sizeInfo);
|
||||
if (size - 0x150 < newSize)
|
||||
{
|
||||
ret = -0xce;
|
||||
goto errout;
|
||||
}
|
||||
if (keyFlag == true)
|
||||
{
|
||||
for (byte i = 0; i < 9; i++)
|
||||
{
|
||||
key.CopyTo(MemlmdMem_BFC00A00.Span[(i * 0x10 + 0x14)..]);
|
||||
MemlmdMem_BFC00A00.Span[i * 0x10 + 0x14] = i;
|
||||
}
|
||||
ref var refHdr = ref MemoryMarshal.AsRef<PSPHeader2>(modData);
|
||||
refHdr.sCheck.Slice(0x30, 0x28).CopyTo(MemlmdMem_2780.Span);
|
||||
refHdr.sCheck.Slice(0x30, 0x28).Fill(0);
|
||||
MemlmdMem_2780.Span.Slice(0, 0x28).CopyTo(local_80);
|
||||
var tmp = size - 4;
|
||||
MemoryMarshal.Write(modData, ref tmp);
|
||||
if (use_polling == 0)
|
||||
{
|
||||
ret = KIRKEngine.sceUtilsBufferCopyWithRange(modData, size, modData, size, KIRKEngine.KIRK_CMD_SHA1_HASH);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
ret = -1;
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
modData.Slice(0, 0x14).CopyTo(local_50);
|
||||
MemlmdKirkMemory.Span.Slice(0, 0x20).CopyTo(modData);
|
||||
key7A90.CopyTo(MemlmdMem_BFC00340);
|
||||
local_50.Slice(0, 0x14).CopyTo(MemlmdMem_BFC00340.Span[0x28..]);
|
||||
local_80.Slice(0, 0x28).CopyTo(MemlmdMem_BFC00340.Span[0x3C..]);
|
||||
if (use_polling == 0)
|
||||
{
|
||||
ret = KIRKEngine.sceUtilsBufferCopyWithRange(null, 0, MemlmdMem_BFC00340.Span, 100, KIRKEngine.KIRK_CMD_ECDSA_VERIFY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
ret = -1;
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
// clear key_4C9494F0 psp 660
|
||||
// clear key_4C9495F0 psp 660
|
||||
// clear key_4C94A1F0 psv
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -0x132;
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ret < 0)
|
||||
{
|
||||
ret = -0x66;
|
||||
}
|
||||
else if (ret == 0xc)
|
||||
{
|
||||
ret = -0x6b;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -0x69;
|
||||
}
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key[..0x90].CopyTo(MemlmdMem_BFC00A00.Span[0x14..]);
|
||||
}
|
||||
ret = Kirk7(MemlmdMem_BFC00A00.Span, 0x90, keySeed.Value, use_polling);
|
||||
if (ret == 0)
|
||||
{
|
||||
MemlmdMem_BFC00A00[..0x90].CopyTo(MemlmdMem_BFC00280);
|
||||
if (keyFlag == true)
|
||||
{
|
||||
hdr.CheckData[..0x5C].CopyTo(MemlmdMem_BFC00A00.Span);
|
||||
hdr.keyData4.CopyTo(MemlmdMem_BFC00A00[0x5C..].Span);
|
||||
hdr.sha1Hash.CopyTo(MemlmdMem_BFC00A00[0x6C..].Span);
|
||||
hdr.keyData.CopyTo(MemlmdMem_BFC00A00[0x80..].Span);
|
||||
hdr.cmacDataHash.CopyTo(MemlmdMem_BFC00A00[0xB0..].Span);
|
||||
hdr.sizeInfo.CopyTo(MemlmdMem_BFC00A00[0xC0..].Span);
|
||||
hdr.RawHdr.CopyTo(MemlmdMem_BFC00A00[0xD0..].Span);
|
||||
MemlmdMem_BFC00A00.Slice(0x34, 0x28).Span.Fill(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hdr.CheckData.CopyTo(MemlmdMem_BFC00A00.Span);
|
||||
hdr.keyData50.CopyTo(MemlmdMem_BFC00A00[0x80..].Span);
|
||||
hdr.RawHdr.CopyTo(MemlmdMem_BFC00A00[0xD0..].Span);
|
||||
}
|
||||
if (keyFlag == true)
|
||||
{
|
||||
MemlmdMem_BFC00A00.Slice(0x5C, 0x60).CopyTo(MemlmdMem_BFC00340[0x14..]);
|
||||
ret = Kirk7(MemlmdMem_BFC00340.Span, 0x60, keySeed.Value, use_polling);
|
||||
if (ret == 0)
|
||||
{
|
||||
MemlmdMem_BFC00340.Slice(0, 0x60).CopyTo(MemlmdMem_BFC00A00[0x5C..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
goto kirkerr;
|
||||
}
|
||||
}
|
||||
if (keyFlag == true)
|
||||
{
|
||||
MemlmdMem_BFC00A00.Slice(0x6C, 0x14).CopyTo(MemlmdMem_BFC00340);
|
||||
MemlmdMem_BFC00A00.Slice(0x5C, 0x10).CopyTo(MemlmdMem_BFC00A00[0x70..]);
|
||||
MemlmdMem_BFC00A00.Slice(0x18, 0x58).Span.Fill(0);
|
||||
MemlmdMem_BFC00A00[..4].CopyTo(MemlmdMem_BFC00A00[4..]);
|
||||
var tmp = 0x14c;
|
||||
MemoryMarshal.Write(MemlmdMem_BFC00A00.Span, ref tmp);
|
||||
MemlmdMem_BFC00280[..0x10].CopyTo(MemlmdMem_BFC00A00[8..]);
|
||||
MemlmdMem_BFC00280[..0x10].Span.Fill(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemlmdMem_BFC00A00.Slice(4, 0x14).CopyTo(MemlmdMem_BFC00340);
|
||||
var tmp = 0x14c;
|
||||
MemoryMarshal.Write(MemlmdMem_BFC00A00.Span, ref tmp);
|
||||
MemlmdMem_BFC00280[..0x14].CopyTo(MemlmdMem_BFC00A00[4..]);
|
||||
}
|
||||
if (use_polling == 0)
|
||||
{
|
||||
ret = KIRKEngine.sceUtilsBufferCopyWithRange(MemlmdMem_BFC00A00.Span, 0x150, MemlmdMem_BFC00A00.Span, 0x150, KIRKEngine.KIRK_CMD_SHA1_HASH);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
ret = -1;
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
if (!MemlmdMem_BFC00A00[..0x14].Span.SequenceEqual(MemlmdMem_BFC00340.Span[..0x14]))
|
||||
{
|
||||
ret = -0x12e;
|
||||
goto errout;
|
||||
}
|
||||
if (keyFlag == true)
|
||||
{
|
||||
for (var i = 0; i < 0x40; i++)
|
||||
{
|
||||
MemlmdMem_BFC00A00[0x80..].Span[i] ^= MemlmdMem_BFC00280[0x10..].Span[i];
|
||||
}
|
||||
MemlmdMem_BFC00280.Slice(0x10, 0x40).Span.Fill(0);
|
||||
ret = Kirk7(MemlmdMem_BFC00A00[0x6C..].Span, 0x40, keySeed.Value, use_polling);
|
||||
if (ret != 0)
|
||||
{
|
||||
goto kirkerr;
|
||||
}
|
||||
for (var i = 0; i < 0x40; i++)
|
||||
{
|
||||
modData[0x40..][i] = (byte)(MemlmdMem_BFC00A00[0x6C..].Span[i] ^ MemlmdMem_BFC00280[0x50..].Span[i]);
|
||||
}
|
||||
MemlmdMem_BFC00280.Slice(0x50, 0x40).Span.Fill(0);
|
||||
modData.Slice(0x80, 0x30).Fill(0);
|
||||
|
||||
ref var cmd1Hdr = ref MemoryMarshal.AsRef<KIRKEngine.KIRK_CMD1_HEADER>(modData[0x40..]);
|
||||
cmd1Hdr.mode = 1;
|
||||
MemlmdMem_BFC00A00.Slice(0xc0, 0x10).Span.CopyTo(cmd1Hdr.off70); // DAT_00009e80
|
||||
modData.Slice(0xc0, 0x10).Fill(0);
|
||||
MemlmdMem_BFC00A00.Slice(0xd0, 0x80).Span.CopyTo(modData[0xd0..]); // psp hdr size 0x80
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 0x70; i++)
|
||||
{
|
||||
MemlmdMem_BFC00A00[0x40..].Span[i] ^= MemlmdMem_BFC00280[0x14..].Span[i];
|
||||
}
|
||||
ret = Kirk7(MemlmdMem_BFC00A00[0x2C..].Span, 0x70, keySeed.Value, use_polling);
|
||||
if (ret == 0)
|
||||
{
|
||||
for (var i = 0; i < 0x70; i++)
|
||||
{
|
||||
modData[0x40..][i] = (byte)(MemlmdMem_BFC00A00[0x2C..].Span[i] ^ MemlmdMem_BFC00280[0x20..].Span[i]);
|
||||
}
|
||||
MemlmdMem_BFC00A00.Slice(0xB0, 0xA0).Span.CopyTo(modData[0xB0..]);
|
||||
ref var cmd1ecdsaHdr = ref MemoryMarshal.AsRef<KIRKEngine.KIRK_CMD1_ECDSA_HEADER>(modData[0x40..]);
|
||||
if (cmd1ecdsaHdr.ecdsa_hash != 1)
|
||||
{
|
||||
ret = -0x12f;
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// goto kirk err;
|
||||
goto kirkerr;
|
||||
}
|
||||
}
|
||||
if (use_polling == 0)
|
||||
{
|
||||
// File.WriteAllBytes("wrongdata", modData.ToArray());
|
||||
ret = KIRKEngine.sceUtilsBufferCopyWithRange(modData, size, modData[0x40..], size - 0x40, KIRKEngine.KIRK_CMD_DECRYPT_PRIVATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
goto rout;
|
||||
}
|
||||
}
|
||||
}
|
||||
errout:
|
||||
|
||||
MemlmdKirkMemory.Span.Fill(0);
|
||||
newSize = 0;
|
||||
rout:
|
||||
MemlmdMem_BFC00A00.Span.Fill(0);
|
||||
|
||||
return ret;
|
||||
kirkerr:
|
||||
ret ^= 0xC;
|
||||
ret = -0x6a;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
static int Kirk7(Span<byte> data, int size, int seed, int use_polling)
|
||||
{
|
||||
KIRKEngine.KIRK_AES128CBC_HEADER hdr = new KIRKEngine.KIRK_AES128CBC_HEADER
|
||||
{
|
||||
mode = KIRKEngine.KIRK_MODE_DECRYPT_CBC,
|
||||
keyseed = seed,
|
||||
data_size = size
|
||||
};
|
||||
MemoryMarshal.Write(data, ref hdr);
|
||||
//using (var ms = new MemoryStream(data))
|
||||
//{
|
||||
// ms.Seek(offset, SeekOrigin.Begin);
|
||||
// using var bw = new BinaryWriter(ms);
|
||||
// bw.Write(KIRKEngine.KIRK_MODE_DECRYPT_CBC);
|
||||
// bw.Write(0);
|
||||
// bw.Write(0);
|
||||
// bw.Write(seed);
|
||||
// bw.Write(size);
|
||||
//}
|
||||
if (use_polling == 0)
|
||||
{
|
||||
KIRKEngine.sceUtilsBufferCopyWithRange(data, size + 20, data, size + 20,
|
||||
KIRKEngine.KIRK_CMD_DECRYPT_IV_0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int Kirk8(Span<byte> buf, int size, int use_polling)
|
||||
{
|
||||
int retv;
|
||||
ref var hdr = ref Utils.AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(buf);
|
||||
hdr.mode = KIRKEngine.KIRK_MODE_DECRYPT_CBC;
|
||||
hdr.keyseed = 0x0100;
|
||||
hdr.data_size = size;
|
||||
|
||||
retv = KIRKEngine.sceUtilsBufferCopyWithRange(buf, size + 0x14, buf, size, KIRKEngine.KIRK_CMD_DECRYPT_IV_FUSE);
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
private static readonly byte[] key_XOR_2304 =
|
||||
{
|
||||
0x71, 0xF6, 0xA8, 0x31, 0x1E, 0xE0, 0xFF, 0x1E, 0x50, 0xBA, 0x6C, 0xD2, 0x98, 0x2D, 0xD6, 0x2D
|
||||
};
|
||||
|
||||
private static readonly byte[] key_XOR_2314 =
|
||||
{
|
||||
0xAA, 0x85, 0x4D, 0xB0, 0xFF, 0xCA, 0x47, 0xEB, 0x38, 0x7F, 0xD7, 0xE4, 0x3D, 0x62, 0xB0, 0x10
|
||||
};
|
||||
|
||||
static int memlmd_6192F715(Span<byte> modData, int size) => KernelModuleSignCheck(modData, size, 0);
|
||||
|
||||
static int memlmd_EA94592C(Span<byte> modData, int size) => KernelModuleSignCheck(modData, size, 1);
|
||||
|
||||
static int KernelModuleSignCheck(Span<byte> modData, int size, int use_polling)
|
||||
{
|
||||
int ret = -0xc9;
|
||||
if (modData.IsEmpty)
|
||||
{
|
||||
goto errorout;
|
||||
}
|
||||
if (size < 0x15F)
|
||||
{
|
||||
ret = -0xca;
|
||||
goto errorout;
|
||||
}
|
||||
modData.Slice(0x80, 0xD0).CopyTo(MemlmdMem_BFC00A00.Span.Slice(0x14));
|
||||
|
||||
for (var i = 0; i < 0xD0; i++)
|
||||
{
|
||||
MemlmdMem_BFC00A00[0x14..].Span[i] ^= key_XOR_2314[i & 0xF];
|
||||
}
|
||||
ret = Kirk8(MemlmdMem_BFC00A00.Span, 0xD0, use_polling);
|
||||
if (ret == 0)
|
||||
{
|
||||
for (var i = 0; i < 0xD0; i++)
|
||||
{
|
||||
MemlmdMem_BFC00A00.Span[i] ^= key_XOR_2304[i & 0xF];
|
||||
}
|
||||
MemlmdMem_BFC00A00.Slice(0x40, 0x90).Span.CopyTo(modData[0x80..]);
|
||||
MemlmdMem_BFC00A00.Slice(0, 0x40).Span.CopyTo(modData[0x110..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ret < 0)
|
||||
{
|
||||
ret = -0x67;
|
||||
}
|
||||
else if (ret != 0xc)
|
||||
{
|
||||
ret = -0x6a;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -0x67;
|
||||
}
|
||||
}
|
||||
errorout:
|
||||
MemlmdMem_BFC00A00.Slice(0, 0x164).Span.Fill(0);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,222 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Formats.Asn1
|
||||
{
|
||||
internal ref struct AsnValueReader
|
||||
{
|
||||
private static readonly byte[] s_singleByte = new byte[1];
|
||||
|
||||
private ReadOnlySpan<byte> _span;
|
||||
private readonly AsnEncodingRules _ruleSet;
|
||||
|
||||
internal AsnValueReader(ReadOnlySpan<byte> span, AsnEncodingRules ruleSet)
|
||||
{
|
||||
_span = span;
|
||||
_ruleSet = ruleSet;
|
||||
}
|
||||
|
||||
internal bool HasData => !_span.IsEmpty;
|
||||
|
||||
internal void ThrowIfNotEmpty()
|
||||
{
|
||||
if (!_span.IsEmpty)
|
||||
{
|
||||
new AsnReader(s_singleByte, _ruleSet).ThrowIfNotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
internal Asn1Tag PeekTag()
|
||||
{
|
||||
return Asn1Tag.Decode(_span, out _);
|
||||
}
|
||||
|
||||
internal ReadOnlySpan<byte> PeekContentBytes()
|
||||
{
|
||||
AsnDecoder.ReadEncodedValue(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out int contentOffset,
|
||||
out int contentLength,
|
||||
out _);
|
||||
|
||||
return _span.Slice(contentOffset, contentLength);
|
||||
}
|
||||
|
||||
internal ReadOnlySpan<byte> PeekEncodedValue()
|
||||
{
|
||||
AsnDecoder.ReadEncodedValue(_span, _ruleSet, out _, out _, out int consumed);
|
||||
return _span.Slice(0, consumed);
|
||||
}
|
||||
|
||||
internal ReadOnlySpan<byte> ReadEncodedValue()
|
||||
{
|
||||
ReadOnlySpan<byte> value = PeekEncodedValue();
|
||||
_span = _span.Slice(value.Length);
|
||||
return value;
|
||||
}
|
||||
|
||||
internal bool ReadBoolean(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
bool ret = AsnDecoder.ReadBoolean(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal BigInteger ReadInteger(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
BigInteger ret = AsnDecoder.ReadInteger(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal bool TryReadInt32(out int value, Asn1Tag? expectedTag = default)
|
||||
{
|
||||
bool ret = AsnDecoder.TryReadInt32(_span, _ruleSet, out value, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal ReadOnlySpan<byte> ReadIntegerBytes(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
ReadOnlySpan<byte> ret = AsnDecoder.ReadIntegerBytes(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal bool TryReadPrimitiveBitString(
|
||||
out int unusedBitCount,
|
||||
out ReadOnlySpan<byte> value,
|
||||
Asn1Tag? expectedTag = default)
|
||||
{
|
||||
bool ret = AsnDecoder.TryReadPrimitiveBitString(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out unusedBitCount,
|
||||
out value,
|
||||
out int consumed,
|
||||
expectedTag);
|
||||
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = default)
|
||||
{
|
||||
byte[] ret = AsnDecoder.ReadBitString(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out unusedBitCount,
|
||||
out int consumed,
|
||||
expectedTag);
|
||||
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal TFlagsEnum ReadNamedBitListValue<TFlagsEnum>(Asn1Tag? expectedTag = default) where TFlagsEnum : Enum
|
||||
{
|
||||
TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue<TFlagsEnum>(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal bool TryReadPrimitiveOctetString(
|
||||
out ReadOnlySpan<byte> value,
|
||||
Asn1Tag? expectedTag = default)
|
||||
{
|
||||
bool ret = AsnDecoder.TryReadPrimitiveOctetString(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out value,
|
||||
out int consumed,
|
||||
expectedTag);
|
||||
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal byte[] ReadOctetString(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
byte[] ret = AsnDecoder.ReadOctetString(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out int consumed,
|
||||
expectedTag);
|
||||
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal string ReadObjectIdentifier(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
string ret = AsnDecoder.ReadObjectIdentifier(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal AsnValueReader ReadSequence(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
AsnDecoder.ReadSequence(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out int contentOffset,
|
||||
out int contentLength,
|
||||
out int bytesConsumed,
|
||||
expectedTag);
|
||||
|
||||
ReadOnlySpan<byte> content = _span.Slice(contentOffset, contentLength);
|
||||
_span = _span.Slice(bytesConsumed);
|
||||
return new AsnValueReader(content, _ruleSet);
|
||||
}
|
||||
|
||||
internal AsnValueReader ReadSetOf(Asn1Tag? expectedTag = default, bool skipSortOrderValidation = false)
|
||||
{
|
||||
AsnDecoder.ReadSetOf(
|
||||
_span,
|
||||
_ruleSet,
|
||||
out int contentOffset,
|
||||
out int contentLength,
|
||||
out int bytesConsumed,
|
||||
skipSortOrderValidation: skipSortOrderValidation,
|
||||
expectedTag: expectedTag);
|
||||
|
||||
ReadOnlySpan<byte> content = _span.Slice(contentOffset, contentLength);
|
||||
_span = _span.Slice(bytesConsumed);
|
||||
return new AsnValueReader(content, _ruleSet);
|
||||
}
|
||||
|
||||
internal DateTimeOffset ReadUtcTime(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
DateTimeOffset ret = AsnDecoder.ReadUtcTime(_span, _ruleSet, out int consumed, expectedTag: expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal DateTimeOffset ReadGeneralizedTime(Asn1Tag? expectedTag = default)
|
||||
{
|
||||
DateTimeOffset ret = AsnDecoder.ReadGeneralizedTime(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = default)
|
||||
{
|
||||
string ret = AsnDecoder.ReadCharacterString(_span, _ruleSet, encodingType, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal TEnum ReadEnumeratedValue<TEnum>(Asn1Tag? expectedTag = null) where TEnum : Enum
|
||||
{
|
||||
TEnum ret = AsnDecoder.ReadEnumeratedValue<TEnum>(_span, _ruleSet, out int consumed, expectedTag);
|
||||
_span = _span.Slice(consumed);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Formats.Asn1;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
//
|
||||
// Common infrastructure for AsymmetricAlgorithm-derived classes that layer on OpenSSL.
|
||||
//
|
||||
internal static partial class AsymmetricAlgorithmHelpers
|
||||
{
|
||||
|
||||
// private static readonly Func<ReadOnlyMemory<byte>, ReadOnlyMemory<byte>[]> ReaderAsn;
|
||||
|
||||
// static AsymmetricAlgorithmHelpers()
|
||||
// {
|
||||
// var assembly = typeof(Aes).Assembly;
|
||||
// var r = assembly.GetType("System.Security.Cryptography.Asn1.AsnReader");
|
||||
// var par1Type = typeof(ReadOnlyMemory<byte>);
|
||||
// var par2Type = assembly.GetType("System.Security.Cryptography.Asn1.AsnEncodingRules");
|
||||
// var asn1TagType = assembly.GetType("System.Security.Cryptography.Asn1.Asn1Tag");
|
||||
// var constructor = r.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.HasThis,
|
||||
// new[] { par1Type, par2Type }, new ParameterModifier[0]);
|
||||
// if (constructor == null)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// var readSequence = r.GetMethod("ReadSequence");
|
||||
// if (readSequence == null)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// var throwIfNotEmpty = r.GetMethod("ThrowIfNotEmpty");
|
||||
// if (throwIfNotEmpty == null)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// var readIntegerBytes = r.GetMethod("ReadIntegerBytes", BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.HasThis, new Type[0], new ParameterModifier[0]);
|
||||
// if (readIntegerBytes == null)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// var par1 = Expression.Parameter(par1Type, "data");
|
||||
// var derField = par2Type.GetField("DER");
|
||||
// var readerVar = Expression.Variable(r, "reader");
|
||||
// var sequenceReaderVar = Expression.Variable(r, "sequenceReader");
|
||||
// var rDerVar = Expression.Variable(typeof(ReadOnlyMemory<byte>), "rDer");
|
||||
// var sDerVar = Expression.Variable(typeof(ReadOnlyMemory<byte>), "sDer");
|
||||
// var sequenceField = asn1TagType.GetField("Sequence");
|
||||
// var expBlock = Expression.Block(new[] { readerVar, sequenceReaderVar, rDerVar, sDerVar },
|
||||
// Expression.Assign(readerVar, Expression.New(constructor, par1, Expression.Field(null, derField))),
|
||||
// Expression.Assign(sequenceReaderVar, Expression.Call(readerVar, readSequence, Expression.Field(null, sequenceField))),
|
||||
// Expression.Call(readerVar, throwIfNotEmpty),
|
||||
// Expression.Assign(rDerVar, Expression.Call(sequenceReaderVar, readIntegerBytes)),
|
||||
// Expression.Assign(sDerVar, Expression.Call(sequenceReaderVar, readIntegerBytes)),
|
||||
// Expression.Call(sequenceReaderVar, throwIfNotEmpty),
|
||||
// Expression.NewArrayInit(typeof(ReadOnlyMemory<byte>), rDerVar, sDerVar));
|
||||
// ReaderAsn = Expression.Lambda<Func<ReadOnlyMemory<byte>, ReadOnlyMemory<byte>[]>>(expBlock, par1).Compile();
|
||||
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Convert Der format of (r, s) to Ieee1363 format
|
||||
/// </summary>
|
||||
public static byte[] ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits)
|
||||
{
|
||||
int fieldSizeBytes = BitsToBytes(fieldSizeBits);
|
||||
int encodedSize = 2 * fieldSizeBytes;
|
||||
byte[] response = new byte[encodedSize];
|
||||
|
||||
ConvertDerToIeee1363(input, fieldSizeBits, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
internal static int ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits, Span<byte> destination)
|
||||
{
|
||||
int fieldSizeBytes = BitsToBytes(fieldSizeBits);
|
||||
int encodedSize = 2 * fieldSizeBytes;
|
||||
|
||||
Debug.Assert(destination.Length >= encodedSize);
|
||||
|
||||
try
|
||||
{
|
||||
AsnValueReader reader = new AsnValueReader(input, AsnEncodingRules.DER);
|
||||
AsnValueReader sequenceReader = reader.ReadSequence();
|
||||
reader.ThrowIfNotEmpty();
|
||||
ReadOnlySpan<byte> rDer = sequenceReader.ReadIntegerBytes();
|
||||
ReadOnlySpan<byte> sDer = sequenceReader.ReadIntegerBytes();
|
||||
sequenceReader.ThrowIfNotEmpty();
|
||||
|
||||
CopySignatureField(rDer, destination.Slice(0, fieldSizeBytes));
|
||||
CopySignatureField(sDer, destination.Slice(fieldSizeBytes, fieldSizeBytes));
|
||||
return encodedSize;
|
||||
}
|
||||
catch (AsnContentException e)
|
||||
{
|
||||
throw new CryptographicException("ASN1 corrupted data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static int BitsToBytes(int bitLength)
|
||||
{
|
||||
int byteLength = (bitLength + 7) / 8;
|
||||
return byteLength;
|
||||
}
|
||||
|
||||
private static void CopySignatureField(ReadOnlySpan<byte> signatureField, Span<byte> response)
|
||||
{
|
||||
if (signatureField.Length > response.Length)
|
||||
{
|
||||
if (signatureField.Length != response.Length + 1 ||
|
||||
signatureField[0] != 0 ||
|
||||
signatureField[1] <= 0x7F)
|
||||
{
|
||||
// The only way this should be true is if the value required a zero-byte-pad.
|
||||
Debug.Fail($"A signature field was longer ({signatureField.Length}) than expected ({response.Length})");
|
||||
throw new CryptographicException();
|
||||
}
|
||||
|
||||
signatureField = signatureField.Slice(1);
|
||||
}
|
||||
|
||||
// If the field is too short then it needs to be prepended
|
||||
// with zeroes in the response. Since the array was already
|
||||
// zeroed out, just figure out where we need to start copying.
|
||||
int writeOffset = response.Length - signatureField.Length;
|
||||
response.Slice(0, writeOffset).Clear();
|
||||
signatureField.CopyTo(response.Slice(writeOffset));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Signers;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Math.EC;
|
||||
using Org.BouncyCastle.Security;
|
||||
using System;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
internal class ECDsaManaged : System.Security.Cryptography.ECDsa
|
||||
{
|
||||
private ECKeyParameters _ecKeyParameters;
|
||||
private readonly bool _ebootPbp;
|
||||
private readonly int _type;
|
||||
|
||||
public ECDsaManaged()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ECDsaManaged(System.Security.Cryptography.ECParameters parameters, bool ebootPbp, int type)
|
||||
{
|
||||
_ebootPbp = ebootPbp;
|
||||
_type = type;
|
||||
var gx = new BigInteger(1, parameters.Curve.G.X);
|
||||
var gy = new BigInteger(1, parameters.Curve.G.Y);
|
||||
var curve = ConvertECCurve(parameters.Curve);
|
||||
var g = curve.CreatePoint(gx, gy);
|
||||
var domainParameters = new ECDomainParameters(curve, g, curve.Order);
|
||||
if (parameters.D != null)
|
||||
{
|
||||
var privateKey = new BigInteger(1, parameters.D);
|
||||
_ecKeyParameters = new ECPrivateKeyParameters(privateKey, domainParameters);
|
||||
}
|
||||
else if (parameters.Q.X != null && parameters.Q.Y != null)
|
||||
{
|
||||
var publicKey = curve.CreatePoint(new BigInteger(1, parameters.Q.X), new BigInteger(1, parameters.Q.Y));
|
||||
_ecKeyParameters = new ECPublicKeyParameters(publicKey, domainParameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("invalid parameters", nameof(parameters));
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] SignHash(byte[] hash)
|
||||
{
|
||||
if (_ecKeyParameters is not ECPrivateKeyParameters)
|
||||
{
|
||||
throw new ArgumentException("key is not private Key");
|
||||
}
|
||||
var signer = CreateSigner();
|
||||
signer.Init(true, _ecKeyParameters);
|
||||
signer.BlockUpdate(hash);
|
||||
return signer.GenerateSignature();
|
||||
}
|
||||
|
||||
public override bool VerifyHash(byte[] hash, byte[] signature)
|
||||
{
|
||||
var signer = CreateSigner();
|
||||
if (_ecKeyParameters is ECPrivateKeyParameters ecPrivateKeyParameters)
|
||||
{
|
||||
var publicKey = new ECPublicKeyParameters(
|
||||
ecPrivateKeyParameters.Parameters.G.Multiply(ecPrivateKeyParameters.D),
|
||||
ecPrivateKeyParameters.Parameters);
|
||||
signer.Init(false, publicKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
signer.Init(false, _ecKeyParameters);
|
||||
}
|
||||
signer.BlockUpdate(hash);
|
||||
return signer.VerifySignature(signature);
|
||||
}
|
||||
|
||||
protected override byte[] HashData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm)
|
||||
{
|
||||
var dataSpan = data.AsSpan().Slice(offset, count);
|
||||
if (hashAlgorithm == System.Security.Cryptography.HashAlgorithmName.SHA256)
|
||||
{
|
||||
return System.Security.Cryptography.SHA256.HashData(dataSpan);
|
||||
}
|
||||
else if (hashAlgorithm == System.Security.Cryptography.HashAlgorithmName.SHA1)
|
||||
{
|
||||
return System.Security.Cryptography.SHA1.HashData(dataSpan);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"{hashAlgorithm} not supported");
|
||||
}
|
||||
}
|
||||
|
||||
private ISigner CreateSigner()
|
||||
{
|
||||
IDigest digest = DigestUtilities.GetDigest("NONE");
|
||||
IDsa dsa = _ebootPbp ? new ECDsaSigner(new EbootPbpKCalculator(_type)) : new ECDsaSigner();
|
||||
var signer = new DsaDigestSigner(dsa, digest, PlainDsaEncoding.Instance);
|
||||
return signer;
|
||||
}
|
||||
|
||||
private FpCurve _fpCurve;
|
||||
|
||||
public override void GenerateKey(System.Security.Cryptography.ECCurve curve)
|
||||
{
|
||||
_fpCurve = ConvertECCurve(curve);
|
||||
var gx = new BigInteger(1, curve.G.X);
|
||||
var gy = new BigInteger(1, curve.G.Y);
|
||||
var g = _fpCurve.CreatePoint(gx, gy);
|
||||
var domainParameters = new ECDomainParameters(_fpCurve, g, _fpCurve.Order);
|
||||
var gen = new ECKeyPairGenerator();
|
||||
gen.Init(new ECKeyGenerationParameters(domainParameters, new SecureRandom()));
|
||||
var keyPair = gen.GenerateKeyPair();
|
||||
_ecKeyParameters = (ECKeyParameters)keyPair.Private;
|
||||
}
|
||||
|
||||
public override System.Security.Cryptography.ECParameters ExportExplicitParameters(bool includePrivateParameters)
|
||||
{
|
||||
var normalG = _ecKeyParameters.Parameters.G;
|
||||
var curve = new System.Security.Cryptography.ECCurve
|
||||
{
|
||||
A = _fpCurve.A.ToBigInteger().ToByteArrayUnsigned(),
|
||||
B = _fpCurve.B.ToBigInteger().ToByteArrayUnsigned(),
|
||||
Prime = _fpCurve.Q.ToByteArrayUnsigned(),
|
||||
Order = _fpCurve.Order.ToByteArrayUnsigned(),
|
||||
Cofactor = _fpCurve.Cofactor.ToByteArrayUnsigned(),
|
||||
G = new System.Security.Cryptography.ECPoint
|
||||
{
|
||||
X = normalG.XCoord.ToBigInteger().ToByteArrayUnsigned(),
|
||||
Y = normalG.YCoord.ToBigInteger().ToByteArrayUnsigned()
|
||||
}
|
||||
};
|
||||
var parameters = new System.Security.Cryptography.ECParameters
|
||||
{
|
||||
Curve = curve
|
||||
};
|
||||
if (includePrivateParameters && _ecKeyParameters is ECPrivateKeyParameters privateKeyParameters)
|
||||
{
|
||||
parameters.D = privateKeyParameters.D.ToByteArrayUnsigned();
|
||||
Console.WriteLine(privateKeyParameters.D.ToString(16).ToUpper());
|
||||
var publicKey = privateKeyParameters.Parameters.G.Multiply(privateKeyParameters.D).Normalize();
|
||||
parameters.Q = new System.Security.Cryptography.ECPoint
|
||||
{
|
||||
X = publicKey.XCoord.ToBigInteger().ToByteArrayUnsigned(),
|
||||
Y = publicKey.YCoord.ToBigInteger().ToByteArrayUnsigned()
|
||||
};
|
||||
}
|
||||
else if (_ecKeyParameters is ECPublicKeyParameters publicKeyParameters)
|
||||
{
|
||||
var publicKey = publicKeyParameters.Q;
|
||||
parameters.Q = new System.Security.Cryptography.ECPoint
|
||||
{
|
||||
X = publicKey.XCoord.ToBigInteger().ToByteArrayUnsigned(),
|
||||
Y = publicKey.YCoord.ToBigInteger().ToByteArrayUnsigned()
|
||||
};
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static FpCurve ConvertECCurve(System.Security.Cryptography.ECCurve curve)
|
||||
{
|
||||
var p = new BigInteger(1, curve.Prime);
|
||||
var a = new BigInteger(1, curve.A);
|
||||
var b = new BigInteger(1, curve.B);
|
||||
var n = new BigInteger(1, curve.Order);
|
||||
var fpCurve = new FpCurve(p, a, b, n, BigInteger.One);
|
||||
return fpCurve;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Signers;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Security;
|
||||
using System;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
internal class EbootPbpKCalculator : IDsaKCalculator
|
||||
{
|
||||
private int _type;
|
||||
|
||||
private BigInteger _n;
|
||||
|
||||
public EbootPbpKCalculator(int type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public bool IsDeterministic => true;
|
||||
|
||||
private readonly Memory<byte> _hash = new byte[0x40];
|
||||
|
||||
public void Init(BigInteger n, SecureRandom random)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public void Init(BigInteger n, BigInteger d, byte[] message)
|
||||
{
|
||||
_n = n;
|
||||
Span<byte> hmacIn = stackalloc byte[0x38];
|
||||
message[..0x1C].CopyTo(hmacIn);
|
||||
KeyVault.Eboot_priv[_type].CopyTo(hmacIn[0x1C..]);
|
||||
|
||||
var hmac = new HMac(new Sha256Digest());
|
||||
hmac.Init(new KeyParameter(KeyVault.Eboot_hmacKey));
|
||||
hmac.BlockUpdate(hmacIn);
|
||||
var hmac_hash_iv = new byte[hmac.GetMacSize()];
|
||||
hmac.DoFinal(hmac_hash_iv);
|
||||
|
||||
int ret;
|
||||
do
|
||||
{
|
||||
ret = can_be_reversed_80C17A(message, 0x1c, hmac_hash_iv, _hash.Span);
|
||||
if (ret != 0 || (ret = can_be_reversed_80C17A(message, 0x1c, hmac_hash_iv, _hash.Span[0x20..])) != 0)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
} while (ret != 0);
|
||||
}
|
||||
|
||||
public BigInteger NextK()
|
||||
{
|
||||
var bn = new BigInteger(1, _hash.Span[..0x3c]);
|
||||
var ret = bn.Mod(_n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private static int can_be_reversed_80C17A(Span<byte> src, int some_size, Span<byte> iv,
|
||||
Span<byte> src_xored_digest)
|
||||
{
|
||||
Span<byte> src_xored = stackalloc byte[0x20];
|
||||
iv.CopyTo(src_xored);
|
||||
|
||||
if (some_size > 0x20)
|
||||
{
|
||||
return 0x12;
|
||||
}
|
||||
|
||||
for (int i = 0; i < some_size; i++)
|
||||
{
|
||||
src_xored[i] ^= src[i];
|
||||
}
|
||||
|
||||
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
||||
var hash = sha256.ComputeHash(src_xored.ToArray());
|
||||
hash.CopyTo(src_xored_digest);
|
||||
|
||||
for (int i = 0; i < 0x20; i++)
|
||||
{
|
||||
iv[i] ^= src_xored_digest[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 0x20; i++)
|
||||
{
|
||||
if (iv[i] != 0xFF)
|
||||
{
|
||||
iv[i] += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
iv[i] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
//
|
||||
// This class provides the common functionality for HMACSHA1, HMACSHA256, HMACMD5, etc.
|
||||
// Ideally, this would be encapsulated in a common base class but the preexisting contract
|
||||
// locks these public classes into deriving directly from HMAC so we have to use encapsulation
|
||||
// and delegation to HMACCommon instead.
|
||||
//
|
||||
// This wrapper adds the ability to change the Key on the fly for compat with the desktop.
|
||||
//
|
||||
internal sealed class HMACCommon
|
||||
{
|
||||
public HMACCommon(string hashAlgorithmId, byte[] key, int blockSize) :
|
||||
this(hashAlgorithmId, (ReadOnlySpan<byte>)key, blockSize)
|
||||
{
|
||||
// If the key is smaller than the block size, the delegated ctor won't have initialized ActualKey,
|
||||
// so set it here as would ChangeKey.
|
||||
ActualKey ??= key;
|
||||
}
|
||||
|
||||
internal HMACCommon(string hashAlgorithmId, ReadOnlySpan<byte> key, int blockSize)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(hashAlgorithmId));
|
||||
Debug.Assert(blockSize > 0 || blockSize == -1);
|
||||
|
||||
_hashAlgorithmId = hashAlgorithmId;
|
||||
_blockSize = blockSize;
|
||||
|
||||
// note: will not set ActualKey if key size is smaller or equal than blockSize
|
||||
// this is to avoid extra allocation. ActualKey can still be used if key is generated.
|
||||
// Otherwise the ReadOnlySpan overload would actually be slower than byte array overload.
|
||||
ActualKey = ChangeKeyImpl(key);
|
||||
}
|
||||
|
||||
public int HashSizeInBits => _hMacProvider.HashSizeInBytes * 8;
|
||||
public int HashSizeInBytes => _hMacProvider.HashSizeInBytes;
|
||||
|
||||
public void ChangeKey(byte[] key)
|
||||
{
|
||||
ActualKey = ChangeKeyImpl(key) ?? key;
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(_hMacProvider))]
|
||||
private byte[] ChangeKeyImpl(ReadOnlySpan<byte> key)
|
||||
{
|
||||
byte[] modifiedKey = null;
|
||||
|
||||
// If _blockSize is -1 the key isn't going to be extractable by the object holder,
|
||||
// so there's no point in recalculating it in managed code.
|
||||
if (key.Length > _blockSize && _blockSize > 0)
|
||||
{
|
||||
// Perform RFC 2104, section 2 key adjustment.
|
||||
modifiedKey = _hashAlgorithmId switch
|
||||
{
|
||||
"SHA224" => SHA224.HashData(key),
|
||||
_ => throw new CryptographicException(string.Format("'{0}' is not a known hash algorithm.", _hashAlgorithmId)),
|
||||
};
|
||||
}
|
||||
|
||||
HashProvider oldHashProvider = _hMacProvider;
|
||||
_hMacProvider = null!;
|
||||
oldHashProvider?.Dispose(true);
|
||||
_hMacProvider = HashProviderDispenser.CreateMacProvider(_hashAlgorithmId, key);
|
||||
|
||||
return modifiedKey;
|
||||
}
|
||||
|
||||
// The actual key used for hashing. This will not be the same as the original key passed to ChangeKey() if the original key exceeded the
|
||||
// hash algorithm's block size. (See RFC 2104, section 2)
|
||||
public byte[] ActualKey { get; private set; }
|
||||
|
||||
// Adds new data to be hashed. This can be called repeatedly in order to hash data from noncontiguous sources.
|
||||
public void AppendHashData(byte[] data, int offset, int count) =>
|
||||
_hMacProvider.AppendHashData(data, offset, count);
|
||||
|
||||
public void AppendHashData(ReadOnlySpan<byte> source) =>
|
||||
_hMacProvider.AppendHashData(source);
|
||||
|
||||
// Compute the hash based on the appended data and resets the HashProvider for more hashing.
|
||||
public byte[] FinalizeHashAndReset() =>
|
||||
_hMacProvider.FinalizeHashAndReset();
|
||||
|
||||
public int FinalizeHashAndReset(Span<byte> destination) =>
|
||||
_hMacProvider.FinalizeHashAndReset(destination);
|
||||
|
||||
public bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten) =>
|
||||
_hMacProvider.TryFinalizeHashAndReset(destination, out bytesWritten);
|
||||
|
||||
public int GetCurrentHash(Span<byte> destination) =>
|
||||
_hMacProvider.GetCurrentHash(destination);
|
||||
|
||||
public void Reset() => _hMacProvider.Reset();
|
||||
|
||||
public void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_hMacProvider?.Dispose(true);
|
||||
_hMacProvider = null!;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string _hashAlgorithmId;
|
||||
private HashProvider _hMacProvider;
|
||||
private readonly int _blockSize;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
internal sealed class HMACManagedHashProvider : HashProvider
|
||||
{
|
||||
private bool _hashing;
|
||||
private readonly int _blockSizeValue;
|
||||
private readonly int _hashSizeValue;
|
||||
|
||||
private readonly byte[] _key;
|
||||
private readonly HashProvider _hash1;
|
||||
private readonly HashProvider _hash2;
|
||||
|
||||
public HMACManagedHashProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
|
||||
{
|
||||
_hash1 = HashProviderDispenser.CreateHashProvider(hashAlgorithmId);
|
||||
_hash2 = HashProviderDispenser.CreateHashProvider(hashAlgorithmId);
|
||||
|
||||
_blockSizeValue = 64;
|
||||
_hashSizeValue = 224 / 8;
|
||||
|
||||
_key = InitializeKey(key);
|
||||
}
|
||||
|
||||
private byte[] InitializeKey(ReadOnlySpan<byte> key)
|
||||
{
|
||||
if (key.Length > _blockSizeValue)
|
||||
{
|
||||
byte[] result = new byte[_hashSizeValue];
|
||||
_hash1.AppendHashData(key);
|
||||
int written = _hash1.FinalizeHashAndReset(result);
|
||||
Debug.Assert(written == result.Length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return key.ToArray();
|
||||
}
|
||||
|
||||
public override void AppendHashData(ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (!_hashing)
|
||||
{
|
||||
AppendInnerBuffer();
|
||||
_hashing = true;
|
||||
}
|
||||
|
||||
_hash1.AppendHashData(data);
|
||||
}
|
||||
|
||||
public override int FinalizeHashAndReset(Span<byte> destination)
|
||||
{
|
||||
int written = GetCurrentHash(destination);
|
||||
Reset();
|
||||
return written;
|
||||
}
|
||||
|
||||
public override int GetCurrentHash(Span<byte> destination)
|
||||
{
|
||||
if (!_hashing)
|
||||
{
|
||||
AppendInnerBuffer();
|
||||
_hashing = true;
|
||||
}
|
||||
|
||||
// finalize the original hash
|
||||
Span<byte> hashValue1 = stackalloc byte[_hashSizeValue];
|
||||
int hash1Written = _hash1.GetCurrentHash(hashValue1);
|
||||
Debug.Assert(hash1Written == hashValue1.Length);
|
||||
|
||||
// write the outer array
|
||||
AppendOuterBuffer();
|
||||
// write the inner hash and finalize the hash
|
||||
_hash2.AppendHashData(hashValue1);
|
||||
return _hash2.FinalizeHashAndReset(destination);
|
||||
}
|
||||
|
||||
private void AppendInnerBuffer() => AppendPaddingBuffer(0x36, _hash1);
|
||||
private void AppendOuterBuffer() => AppendPaddingBuffer(0x5C, _hash2);
|
||||
|
||||
private void AppendPaddingBuffer(byte paddingConstant, HashProvider hash)
|
||||
{
|
||||
Span<byte> paddingBuffer = stackalloc byte[_blockSizeValue];
|
||||
paddingBuffer.Fill(paddingConstant);
|
||||
|
||||
for (int i = 0; i < _key.Length; i++)
|
||||
{
|
||||
paddingBuffer[i] ^= _key[i];
|
||||
}
|
||||
|
||||
hash.AppendHashData(paddingBuffer);
|
||||
CryptographicOperations.ZeroMemory(paddingBuffer);
|
||||
}
|
||||
|
||||
public override int HashSizeInBytes => _hashSizeValue;
|
||||
|
||||
public override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_hash1.Dispose();
|
||||
_hash2.Dispose();
|
||||
|
||||
CryptographicOperations.ZeroMemory(_key);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
if (_hashing)
|
||||
{
|
||||
_hash1.Reset();
|
||||
_hash2.Reset();
|
||||
_hashing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
public class HMACSHA224 : HMAC
|
||||
{
|
||||
/// <summary>
|
||||
/// The hash size produced by the HMAC SHA224 algorithm, in bits.
|
||||
/// </summary>
|
||||
public const int HashSizeInBits = 224;
|
||||
|
||||
/// <summary>
|
||||
/// The hash size produced by the HMAC SHA24 algorithm, in bytes.
|
||||
/// </summary>
|
||||
public const int HashSizeInBytes = HashSizeInBits / 8;
|
||||
|
||||
public HMACSHA224()
|
||||
: this(RandomNumberGenerator.GetBytes(BlockSize))
|
||||
{ }
|
||||
|
||||
public HMACSHA224(byte[] key)
|
||||
{
|
||||
if (key is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
|
||||
HashName = HashAlgorithmNames.SHA224;
|
||||
_hMacCommon = new HMACCommon(HashAlgorithmNames.SHA224, key, BlockSize);
|
||||
base.Key = _hMacCommon.ActualKey!;
|
||||
// this not really needed as it'll initialize BlockSizeValue with same value it has which is 64.
|
||||
// we just want to be explicit in all HMAC extended classes
|
||||
BlockSizeValue = BlockSize;
|
||||
HashSizeValue = _hMacCommon.HashSizeInBits;
|
||||
Debug.Assert(HashSizeValue == HashSizeInBits);
|
||||
}
|
||||
|
||||
public override byte[] Key
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Key;
|
||||
}
|
||||
set
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
_hMacCommon.ChangeKey(value);
|
||||
base.Key = _hMacCommon.ActualKey!;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HashCore(byte[] rgb, int ib, int cb) =>
|
||||
_hMacCommon.AppendHashData(rgb, ib, cb);
|
||||
|
||||
protected override void HashCore(ReadOnlySpan<byte> source) =>
|
||||
_hMacCommon.AppendHashData(source);
|
||||
|
||||
protected override byte[] HashFinal() =>
|
||||
_hMacCommon.FinalizeHashAndReset();
|
||||
|
||||
protected override bool TryHashFinal(Span<byte> destination, out int bytesWritten) =>
|
||||
_hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten);
|
||||
|
||||
public override void Initialize() => _hMacCommon.Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Computes the HMAC of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="key">The HMAC key.</param>
|
||||
/// <param name="source">The data to HMAC.</param>
|
||||
/// <returns>The HMAC of the data.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="key" /> or <paramref name="source" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public static byte[] HashData(byte[] key, byte[] source)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
return HashData(new ReadOnlySpan<byte>(key), new ReadOnlySpan<byte>(source));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the HMAC of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="key">The HMAC key.</param>
|
||||
/// <param name="source">The data to HMAC.</param>
|
||||
/// <returns>The HMAC of the data.</returns>
|
||||
public static byte[] HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source)
|
||||
{
|
||||
byte[] buffer = new byte[HashSizeInBytes];
|
||||
|
||||
int written = HashData(key, source, buffer.AsSpan());
|
||||
Debug.Assert(written == buffer.Length);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the HMAC of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="key">The HMAC key.</param>
|
||||
/// <param name="source">The data to HMAC.</param>
|
||||
/// <param name="destination">The buffer to receive the HMAC value.</param>
|
||||
/// <returns>The total number of bytes written to <paramref name="destination" />.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// The buffer in <paramref name="destination"/> is too small to hold the calculated hash
|
||||
/// size. The SHA224 algorithm always produces a 224-bit HMAC, or 28 bytes.
|
||||
/// </exception>
|
||||
public static int HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination)
|
||||
{
|
||||
if (!TryHashData(key, source, destination, out int bytesWritten))
|
||||
{
|
||||
throw new ArgumentException("Destination is too short.", nameof(destination));
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to compute the HMAC of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="key">The HMAC key.</param>
|
||||
/// <param name="source">The data to HMAC.</param>
|
||||
/// <param name="destination">The buffer to receive the HMAC value.</param>
|
||||
/// <param name="bytesWritten">
|
||||
/// When this method returns, the total number of bytes written into <paramref name="destination"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <see langword="false"/> if <paramref name="destination"/> is too small to hold the
|
||||
/// calculated hash, <see langword="true"/> otherwise.
|
||||
/// </returns>
|
||||
public static bool TryHashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
|
||||
{
|
||||
if (destination.Length < HashSizeInBytes)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesWritten = HashProviderDispenser.OneShotHashProvider.MacData(HashAlgorithmNames.SHA224, key, source, destination);
|
||||
Debug.Assert(bytesWritten == HashSizeInBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private HMACCommon _hMacCommon;
|
||||
private const int BlockSize = 64;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
internal static class HashAlgorithmNames
|
||||
{
|
||||
public const string SHA224 = "SHA224";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
//
|
||||
// This abstract class represents a reusable hash object and can wrap a CNG or WinRT hash object.
|
||||
//
|
||||
internal abstract class HashProvider : IDisposable
|
||||
{
|
||||
// Adds new data to be hashed. This can be called repeatedly in order to hash data from noncontiguous sources.
|
||||
public void AppendHashData(byte[] data, int offset, int count)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(data);
|
||||
|
||||
// AppendHashData can be called via exposed APIs (e.g. a type that derives from
|
||||
// HMACSHA1 and calls HashCore) and could be passed bad data from there. It could
|
||||
// also receive a bad count from HashAlgorithm reading from a Stream that returns
|
||||
// an invalid number of bytes read. Since our implementations of AppendHashDataCore
|
||||
// end up using unsafe code, we want to be sure the arguments are valid.
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), "Non-negative number required.");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
|
||||
if (data.Length - offset < count)
|
||||
throw new ArgumentException("Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.");
|
||||
|
||||
AppendHashData(new ReadOnlySpan<byte>(data, offset, count));
|
||||
}
|
||||
|
||||
public abstract void AppendHashData(ReadOnlySpan<byte> data);
|
||||
|
||||
// Compute the hash based on the appended data and resets the HashProvider for more hashing.
|
||||
public abstract int FinalizeHashAndReset(Span<byte> destination);
|
||||
|
||||
public abstract int GetCurrentHash(Span<byte> destination);
|
||||
|
||||
public byte[] FinalizeHashAndReset()
|
||||
{
|
||||
byte[] ret = new byte[HashSizeInBytes];
|
||||
|
||||
int written = FinalizeHashAndReset(ret);
|
||||
Debug.Assert(written == HashSizeInBytes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten)
|
||||
{
|
||||
if (destination.Length < HashSizeInBytes)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesWritten = FinalizeHashAndReset(destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the length of the byte array returned by FinalizeHashAndReset.
|
||||
public abstract int HashSizeInBytes { get; }
|
||||
|
||||
// Releases any native resources and keys used by the HashProvider.
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Releases any native resources and keys used by the HashProvider.
|
||||
public abstract void Dispose(bool disposing);
|
||||
|
||||
public abstract void Reset();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
internal class HashProviderDispenser
|
||||
{
|
||||
public static HashProvider CreateHashProvider(string hashAlgorithmId)
|
||||
{
|
||||
switch (hashAlgorithmId)
|
||||
{
|
||||
case HashAlgorithmNames.SHA224:
|
||||
return new SHAManagedHashProvider(hashAlgorithmId);
|
||||
}
|
||||
throw new CryptographicException(string.Format("'{0}' is not a known hash algorithm.", hashAlgorithmId));
|
||||
}
|
||||
|
||||
public static class OneShotHashProvider
|
||||
{
|
||||
public static unsafe int MacData(
|
||||
string hashAlgorithmId,
|
||||
ReadOnlySpan<byte> key,
|
||||
ReadOnlySpan<byte> source,
|
||||
Span<byte> destination)
|
||||
{
|
||||
using HashProvider provider = CreateMacProvider(hashAlgorithmId, key);
|
||||
provider.AppendHashData(source);
|
||||
return provider.FinalizeHashAndReset(destination);
|
||||
}
|
||||
|
||||
public static int HashData(string hashAlgorithmId, ReadOnlySpan<byte> source, Span<byte> destination)
|
||||
{
|
||||
HashProvider provider = CreateHashProvider(hashAlgorithmId);
|
||||
provider.AppendHashData(source);
|
||||
return provider.FinalizeHashAndReset(destination);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
|
||||
{
|
||||
switch (hashAlgorithmId)
|
||||
{
|
||||
case HashAlgorithmNames.SHA224:
|
||||
return new HMACManagedHashProvider(hashAlgorithmId, key);
|
||||
}
|
||||
throw new CryptographicException(string.Format("'{0}' is not a known hash algorithm.", hashAlgorithmId));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
public abstract class SHA224 : HashAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The hash size produced by the SHA224 algorithm, in bits.
|
||||
/// </summary>
|
||||
public const int HashSizeInBits = 224;
|
||||
|
||||
/// <summary>
|
||||
/// The hash size produced by the SHA224 algorithm, in bytes.
|
||||
/// </summary>
|
||||
public const int HashSizeInBytes = HashSizeInBits / 8;
|
||||
|
||||
public SHA224()
|
||||
{
|
||||
// SHA-224 hash length are 224 bits long
|
||||
HashSizeValue = HashSizeInBits;
|
||||
}
|
||||
|
||||
public static new SHA224 Create() => new Implementation();
|
||||
|
||||
public new static SHA224 Create(string hashName)
|
||||
{
|
||||
var o = (SHA224)CryptoConfig.CreateFromName(hashName);
|
||||
// in case machine.config isn't configured to use any SHA224 implementation
|
||||
if (o == null)
|
||||
{
|
||||
o = new Implementation();
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="source">The data to hash.</param>
|
||||
/// <returns>The hash of the data.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="source" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public static byte[] HashData(byte[] source)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
return HashData(new ReadOnlySpan<byte>(source));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="source">The data to hash.</param>
|
||||
/// <returns>The hash of the data.</returns>
|
||||
public static byte[] HashData(ReadOnlySpan<byte> source)
|
||||
{
|
||||
byte[] buffer = GC.AllocateUninitializedArray<byte>(HashSizeInBytes);
|
||||
|
||||
int written = HashData(source, buffer.AsSpan());
|
||||
Debug.Assert(written == buffer.Length);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="source">The data to hash.</param>
|
||||
/// <param name="destination">The buffer to receive the hash value.</param>
|
||||
/// <returns>The total number of bytes written to <paramref name="destination" />.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// The buffer in <paramref name="destination"/> is too small to hold the calculated hash
|
||||
/// size. The SHA224 algorithm always produces a 224-bit hash, or 28 bytes.
|
||||
/// </exception>
|
||||
public static int HashData(ReadOnlySpan<byte> source, Span<byte> destination)
|
||||
{
|
||||
if (!TryHashData(source, destination, out int bytesWritten))
|
||||
throw new ArgumentException("Destination is too short.", nameof(destination));
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to compute the hash of data using the SHA224 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="source">The data to hash.</param>
|
||||
/// <param name="destination">The buffer to receive the hash value.</param>
|
||||
/// <param name="bytesWritten">
|
||||
/// When this method returns, the total number of bytes written into <paramref name="destination"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <see langword="false"/> if <paramref name="destination"/> is too small to hold the
|
||||
/// calculated hash, <see langword="true"/> otherwise.
|
||||
/// </returns>
|
||||
public static bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
|
||||
{
|
||||
if (destination.Length < HashSizeInBytes)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesWritten = HashProviderDispenser.OneShotHashProvider.HashData(HashAlgorithmNames.SHA224, source, destination);
|
||||
Debug.Assert(bytesWritten == HashSizeInBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private sealed class Implementation : SHA224
|
||||
{
|
||||
private readonly HashProvider _hashProvider;
|
||||
|
||||
public Implementation()
|
||||
{
|
||||
_hashProvider = HashProviderDispenser.CreateHashProvider(HashAlgorithmNames.SHA224);
|
||||
HashSizeValue = _hashProvider.HashSizeInBytes * 8;
|
||||
}
|
||||
|
||||
protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) =>
|
||||
_hashProvider.AppendHashData(array, ibStart, cbSize);
|
||||
|
||||
protected sealed override void HashCore(ReadOnlySpan<byte> source) =>
|
||||
_hashProvider.AppendHashData(source);
|
||||
|
||||
protected sealed override byte[] HashFinal() =>
|
||||
_hashProvider.FinalizeHashAndReset();
|
||||
|
||||
protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) =>
|
||||
_hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten);
|
||||
|
||||
public sealed override void Initialize() => _hashProvider.Reset();
|
||||
|
||||
protected sealed override void Dispose(bool disposing)
|
||||
{
|
||||
_hashProvider.Dispose(disposing);
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using static System.Numerics.BitOperations;
|
||||
|
||||
namespace PspCrypto.Security.Cryptography
|
||||
{
|
||||
internal sealed class SHAManagedHashProvider : HashProvider
|
||||
{
|
||||
private int hashSizeInBytes;
|
||||
private SHAManagedImplementationBase impl;
|
||||
private MemoryStream buffer;
|
||||
|
||||
public SHAManagedHashProvider(string hashAlgorithmId)
|
||||
{
|
||||
switch (hashAlgorithmId)
|
||||
{
|
||||
case HashAlgorithmNames.SHA224:
|
||||
impl = new SHA224ManagedImplementation();
|
||||
hashSizeInBytes = 28;
|
||||
break;
|
||||
default:
|
||||
throw new CryptographicException(string.Format("'{0}' is not a known hash algorithm.", hashAlgorithmId));
|
||||
}
|
||||
}
|
||||
|
||||
public override void AppendHashData(ReadOnlySpan<byte> data)
|
||||
{
|
||||
buffer ??= new MemoryStream(1000);
|
||||
|
||||
buffer.Write(data);
|
||||
}
|
||||
|
||||
public override int FinalizeHashAndReset(Span<byte> destination)
|
||||
{
|
||||
GetCurrentHash(destination);
|
||||
buffer = null;
|
||||
|
||||
return hashSizeInBytes;
|
||||
}
|
||||
|
||||
public override int GetCurrentHash(Span<byte> destination)
|
||||
{
|
||||
Debug.Assert(destination.Length >= hashSizeInBytes);
|
||||
|
||||
impl.Initialize();
|
||||
if (buffer != null)
|
||||
{
|
||||
impl.HashCore(buffer.GetBuffer(), 0, (int)buffer.Length);
|
||||
}
|
||||
impl.HashFinal().CopyTo(destination);
|
||||
|
||||
return hashSizeInBytes;
|
||||
}
|
||||
|
||||
public override int HashSizeInBytes => hashSizeInBytes;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
buffer = null;
|
||||
impl.Initialize();
|
||||
}
|
||||
|
||||
public override void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
private abstract class SHAManagedImplementationBase
|
||||
{
|
||||
public abstract void Initialize();
|
||||
public abstract void HashCore(byte[] partIn, int ibStart, int cbSize);
|
||||
public abstract byte[] HashFinal();
|
||||
}
|
||||
|
||||
private sealed class SHA224ManagedImplementation : SHAManagedImplementationBase
|
||||
{
|
||||
private byte[] _buffer;
|
||||
private long _count; // Number of bytes in the hashed message
|
||||
private uint[] _stateSHA224;
|
||||
private uint[] _W;
|
||||
|
||||
public SHA224ManagedImplementation()
|
||||
{
|
||||
_stateSHA224 = new uint[8];
|
||||
_buffer = new byte[64];
|
||||
_W = new uint[64];
|
||||
|
||||
InitializeState();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeState();
|
||||
|
||||
// Zeroize potentially sensitive information.
|
||||
Array.Clear(_buffer, 0, _buffer.Length);
|
||||
Array.Clear(_W, 0, _W.Length);
|
||||
}
|
||||
|
||||
private void InitializeState()
|
||||
{
|
||||
_count = 0;
|
||||
|
||||
_stateSHA224[0] = 0xc1059ed8;
|
||||
_stateSHA224[1] = 0x367cd507;
|
||||
_stateSHA224[2] = 0x3070dd17;
|
||||
_stateSHA224[3] = 0xf70e5939;
|
||||
_stateSHA224[4] = 0xffc00b31;
|
||||
_stateSHA224[5] = 0x68581511;
|
||||
_stateSHA224[6] = 0x64f98fa7;
|
||||
_stateSHA224[7] = 0xbefa4fa4;
|
||||
}
|
||||
|
||||
/* SHA256 block update operation. Continues an SHA message-digest
|
||||
operation, processing another message block, and updating the
|
||||
context.
|
||||
*/
|
||||
public override unsafe void HashCore(byte[] partIn, int ibStart, int cbSize)
|
||||
{
|
||||
int bufferLen;
|
||||
int partInLen = cbSize;
|
||||
int partInBase = ibStart;
|
||||
|
||||
/* Compute length of buffer */
|
||||
bufferLen = (int)(_count & 0x3f);
|
||||
|
||||
/* Update number of bytes */
|
||||
_count += partInLen;
|
||||
|
||||
fixed (uint* stateSHA256 = _stateSHA224)
|
||||
{
|
||||
fixed (byte* buffer = _buffer)
|
||||
{
|
||||
fixed (uint* expandedBuffer = _W)
|
||||
{
|
||||
if (bufferLen > 0 && bufferLen + partInLen >= 64)
|
||||
{
|
||||
Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, 64 - bufferLen);
|
||||
partInBase += 64 - bufferLen;
|
||||
partInLen -= 64 - bufferLen;
|
||||
SHATransform(expandedBuffer, stateSHA256, buffer);
|
||||
bufferLen = 0;
|
||||
}
|
||||
|
||||
/* Copy input to temporary buffer and hash */
|
||||
while (partInLen >= 64)
|
||||
{
|
||||
Buffer.BlockCopy(partIn, partInBase, _buffer, 0, 64);
|
||||
partInBase += 64;
|
||||
partInLen -= 64;
|
||||
SHATransform(expandedBuffer, stateSHA256, buffer);
|
||||
}
|
||||
|
||||
if (partInLen > 0)
|
||||
{
|
||||
Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SHA256 finalization. Ends an SHA256 message-digest operation, writing
|
||||
the message digest.
|
||||
*/
|
||||
public override byte[] HashFinal()
|
||||
{
|
||||
byte[] pad;
|
||||
int padLen;
|
||||
long bitCount;
|
||||
byte[] hash = new byte[28]; // HashSizeValue = 224
|
||||
|
||||
/* Compute padding: 80 00 00 ... 00 00 <bit count>
|
||||
*/
|
||||
|
||||
padLen = 64 - (int)(_count & 0x3f);
|
||||
if (padLen <= 8)
|
||||
padLen += 64;
|
||||
|
||||
pad = new byte[padLen];
|
||||
pad[0] = 0x80;
|
||||
|
||||
// Convert count to bit count
|
||||
bitCount = _count * 8;
|
||||
|
||||
pad[padLen - 8] = (byte)(bitCount >> 56 & 0xff);
|
||||
pad[padLen - 7] = (byte)(bitCount >> 48 & 0xff);
|
||||
pad[padLen - 6] = (byte)(bitCount >> 40 & 0xff);
|
||||
pad[padLen - 5] = (byte)(bitCount >> 32 & 0xff);
|
||||
pad[padLen - 4] = (byte)(bitCount >> 24 & 0xff);
|
||||
pad[padLen - 3] = (byte)(bitCount >> 16 & 0xff);
|
||||
pad[padLen - 2] = (byte)(bitCount >> 8 & 0xff);
|
||||
pad[padLen - 1] = (byte)(bitCount >> 0 & 0xff);
|
||||
|
||||
/* Digest padding */
|
||||
HashCore(pad, 0, pad.Length);
|
||||
|
||||
/* Store digest */
|
||||
SHAUtils.DWORDToBigEndian(hash, _stateSHA224, 7);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
private static readonly uint[] _K = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
private static unsafe void SHATransform(uint* expandedBuffer, uint* state, byte* block)
|
||||
{
|
||||
uint a, b, c, d, e, f, h, g;
|
||||
uint aa, bb, cc, dd, ee, ff, hh, gg;
|
||||
uint T1;
|
||||
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
f = state[5];
|
||||
g = state[6];
|
||||
h = state[7];
|
||||
|
||||
// fill in the first 16 bytes of W.
|
||||
SHAUtils.DWORDFromBigEndian(expandedBuffer, 16, block);
|
||||
SHA256Expand(expandedBuffer);
|
||||
|
||||
/* Apply the SHA256 compression function */
|
||||
// We are trying to be smart here and avoid as many copies as we can
|
||||
// The perf gain with this method over the straightforward modify and shift
|
||||
// forward is >= 20%, so it's worth the pain
|
||||
for (int j = 0; j < 64;)
|
||||
{
|
||||
T1 = h + Sigma_1(e) + Ch(e, f, g) + _K[j] + expandedBuffer[j];
|
||||
ee = d + T1;
|
||||
aa = T1 + Sigma_0(a) + Maj(a, b, c);
|
||||
j++;
|
||||
|
||||
T1 = g + Sigma_1(ee) + Ch(ee, e, f) + _K[j] + expandedBuffer[j];
|
||||
ff = c + T1;
|
||||
bb = T1 + Sigma_0(aa) + Maj(aa, a, b);
|
||||
j++;
|
||||
|
||||
T1 = f + Sigma_1(ff) + Ch(ff, ee, e) + _K[j] + expandedBuffer[j];
|
||||
gg = b + T1;
|
||||
cc = T1 + Sigma_0(bb) + Maj(bb, aa, a);
|
||||
j++;
|
||||
|
||||
T1 = e + Sigma_1(gg) + Ch(gg, ff, ee) + _K[j] + expandedBuffer[j];
|
||||
hh = a + T1;
|
||||
dd = T1 + Sigma_0(cc) + Maj(cc, bb, aa);
|
||||
j++;
|
||||
|
||||
T1 = ee + Sigma_1(hh) + Ch(hh, gg, ff) + _K[j] + expandedBuffer[j];
|
||||
h = aa + T1;
|
||||
d = T1 + Sigma_0(dd) + Maj(dd, cc, bb);
|
||||
j++;
|
||||
|
||||
T1 = ff + Sigma_1(h) + Ch(h, hh, gg) + _K[j] + expandedBuffer[j];
|
||||
g = bb + T1;
|
||||
c = T1 + Sigma_0(d) + Maj(d, dd, cc);
|
||||
j++;
|
||||
|
||||
T1 = gg + Sigma_1(g) + Ch(g, h, hh) + _K[j] + expandedBuffer[j];
|
||||
f = cc + T1;
|
||||
b = T1 + Sigma_0(c) + Maj(c, d, dd);
|
||||
j++;
|
||||
|
||||
T1 = hh + Sigma_1(f) + Ch(f, g, h) + _K[j] + expandedBuffer[j];
|
||||
e = dd + T1;
|
||||
a = T1 + Sigma_0(b) + Maj(b, c, d);
|
||||
j++;
|
||||
}
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
}
|
||||
|
||||
private static uint Ch(uint x, uint y, uint z)
|
||||
{
|
||||
return x & y ^ (x ^ 0xffffffff) & z;
|
||||
}
|
||||
|
||||
private static uint Maj(uint x, uint y, uint z)
|
||||
{
|
||||
return x & y ^ x & z ^ y & z;
|
||||
}
|
||||
|
||||
private static uint sigma_0(uint x)
|
||||
{
|
||||
return RotateRight(x, 7) ^ RotateRight(x, 18) ^ x >> 3;
|
||||
}
|
||||
|
||||
private static uint sigma_1(uint x)
|
||||
{
|
||||
return RotateRight(x, 17) ^ RotateRight(x, 19) ^ x >> 10;
|
||||
}
|
||||
|
||||
private static uint Sigma_0(uint x)
|
||||
{
|
||||
return RotateRight(x, 2) ^ RotateRight(x, 13) ^ RotateRight(x, 22);
|
||||
}
|
||||
|
||||
private static uint Sigma_1(uint x)
|
||||
{
|
||||
return RotateRight(x, 6) ^ RotateRight(x, 11) ^ RotateRight(x, 25);
|
||||
}
|
||||
|
||||
/* This function creates W_16,...,W_63 according to the formula
|
||||
W_j <- sigma_1(W_{j-2}) + W_{j-7} + sigma_0(W_{j-15}) + W_{j-16};
|
||||
*/
|
||||
private static unsafe void SHA256Expand(uint* x)
|
||||
{
|
||||
for (int i = 16; i < 64; i++)
|
||||
{
|
||||
x[i] = sigma_1(x[i - 2]) + x[i - 7] + sigma_0(x[i - 15]) + x[i - 16];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SHAUtils
|
||||
{
|
||||
// digits == number of DWORDs
|
||||
public static unsafe void DWORDFromBigEndian(uint* x, int digits, byte* block)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0, j = 0; i < digits; i++, j += 4)
|
||||
x[i] = (uint)(block[j] << 24 | block[j + 1] << 16 | block[j + 2] << 8 | block[j + 3]);
|
||||
}
|
||||
|
||||
// encodes x (DWORD) into block (unsigned char), most significant byte first.
|
||||
// digits == number of DWORDs
|
||||
public static void DWORDToBigEndian(byte[] block, uint[] x, int digits)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0, j = 0; i < digits; i++, j += 4)
|
||||
{
|
||||
block[j] = (byte)(x[i] >> 24 & 0xff);
|
||||
block[j + 1] = (byte)(x[i] >> 16 & 0xff);
|
||||
block[j + 2] = (byte)(x[i] >> 8 & 0xff);
|
||||
block[j + 3] = (byte)(x[i] & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public enum SceExecFileDecryptMode : byte
|
||||
{
|
||||
/* Not an executable. */
|
||||
DECRYPT_MODE_NO_EXEC = 0,
|
||||
/* 1.50 Kernel module. */
|
||||
DECRYPT_MODE_BOGUS_MODULE = 1,
|
||||
DECRYPT_MODE_KERNEL_MODULE = 2,
|
||||
DECRYPT_MODE_VSH_MODULE = 3,
|
||||
DECRYPT_MODE_USER_MODULE = 4,
|
||||
DECRYPT_MODE_UMD_GAME_EXEC = 9,
|
||||
DECRYPT_MODE_GAMESHARING_EXEC = 10,
|
||||
/* USB/WLAN module. */
|
||||
DECRYPT_MODE_UNKNOWN_11 = 11,
|
||||
DECRYPT_MODE_MS_UPDATER = 12,
|
||||
DECRYPT_MODE_DEMO_EXEC = 13,
|
||||
DECRYPT_MODE_APP_MODULE = 14,
|
||||
DECRYPT_MODE_UNKNOWN_18 = 18,
|
||||
DECRYPT_MODE_UNKNOWN_19 = 19,
|
||||
DECRYPT_MODE_POPS_EXEC = 20,
|
||||
/* MS module. */
|
||||
DECRYPT_MODE_UNKNOWN_21 = 21,
|
||||
/* APP module. */
|
||||
DECRYPT_MODE_UNKNOWN_22 = 22,
|
||||
/* USER module. */
|
||||
DECRYPT_MODE_UNKNOWN_23 = 23,
|
||||
/* USER module. */
|
||||
DECRYPT_MODE_UNKNOWN_25 = 25,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public unsafe struct PSPHeader2
|
||||
{
|
||||
public Span<byte> RawHdr
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (uint* ptr = &magic)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
public uint magic;
|
||||
public ushort modAttribute;
|
||||
public ushort compAttribute;
|
||||
public byte moduleVerLo;
|
||||
public byte moduleVerHi;
|
||||
private fixed byte _modName[27];
|
||||
|
||||
public Span<byte> modName
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _modName)
|
||||
{
|
||||
return new Span<byte>(ptr, 27);
|
||||
}
|
||||
}
|
||||
}
|
||||
public byte terminal;
|
||||
public byte modVersion;
|
||||
public byte nSegments;
|
||||
public int elfSize;
|
||||
public int pspSize;
|
||||
public uint bootEntry;
|
||||
public int modInfoOffset;
|
||||
public uint bssSize;
|
||||
private fixed ushort _segAlign[4];
|
||||
|
||||
public Span<ushort> segAlign
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (ushort* ptr = _segAlign)
|
||||
{
|
||||
return new Span<ushort>(ptr, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed uint _segAddress[4];
|
||||
|
||||
public Span<uint> segAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (uint* ptr = _segAddress)
|
||||
{
|
||||
return new Span<uint>(ptr, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed uint _segSize[4];
|
||||
|
||||
public Span<uint> segSize
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (uint* ptr = _segSize)
|
||||
{
|
||||
return new Span<uint>(ptr, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
public fixed uint reserved[5];
|
||||
public uint devkitVersion;
|
||||
public SceExecFileDecryptMode decryptMode;
|
||||
public byte padding;
|
||||
public ushort overlapSize;
|
||||
private fixed byte _aesKey[16];
|
||||
public Span<byte> aesKey
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _aesKey)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> keyData
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _aesKey)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> keyData50
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _aesKey)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fixed byte _cmacKey[16];
|
||||
public Span<byte> cmacKey
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _cmacKey)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _cmacHeaderHash[16];
|
||||
public Span<byte> cmacHeaderHash
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _cmacHeaderHash)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> sizeInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (int* ptr = &dataSize)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
public int dataSize;
|
||||
public int dataOffset;
|
||||
public uint unk184;
|
||||
public uint unk188;
|
||||
private fixed byte _cmacDataHash[16];
|
||||
|
||||
public Span<byte> cmacDataHash
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _cmacDataHash)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> CheckData
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (uint* ptr = &tag)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
public uint tag;
|
||||
private fixed byte _sCheck[0x58];
|
||||
|
||||
public Span<byte> sCheck
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _sCheck)
|
||||
{
|
||||
return new Span<byte>(ptr, 0x58);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _sha1Hash[20];
|
||||
|
||||
public Span<byte> sha1Hash
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _sha1Hash)
|
||||
{
|
||||
return new Span<byte>(ptr, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
private fixed byte _keyData4[16];
|
||||
|
||||
public Span<byte> keyData4
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = _keyData4)
|
||||
{
|
||||
return new Span<byte>(ptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct PbpHeader
|
||||
{
|
||||
public int Sig;
|
||||
public int Version;
|
||||
public int ParamOff;
|
||||
public int Icon0Off;
|
||||
public int Icon1Off;
|
||||
public int Pic0Off;
|
||||
public int Pic1Off;
|
||||
public int Snd0Off;
|
||||
public int DataPspOff;
|
||||
public int DataPsarOff;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
public static bool isEmpty(Span<byte> buf, int buf_size)
|
||||
{
|
||||
if (buf != null && buf.Length >= buf_size)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < buf_size; i++)
|
||||
{
|
||||
if (buf[i] != 0) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-interprets a span of bytes as a reference to structure of type T.
|
||||
/// The type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T AsRef<T>(Span<byte> span)
|
||||
where T : struct
|
||||
{
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Cannot use type '{typeof(T)}'. Only value types without pointers or references are supported.");
|
||||
}
|
||||
|
||||
if (Unsafe.SizeOf<T>() > (uint)span.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("length");
|
||||
}
|
||||
return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-interprets a span of bytes as a reference to structure of type T.
|
||||
/// The type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T AsRef<T>(ReadOnlySpan<byte> span)
|
||||
where T : struct
|
||||
{
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Cannot use type '{typeof(T)}'. Only value types without pointers or references are supported.");
|
||||
}
|
||||
|
||||
if (Unsafe.SizeOf<T>() > (uint)span.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("length");
|
||||
}
|
||||
return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
|
||||
public static void BuildDrmBBMacFinal2(Span<byte> mac)
|
||||
{
|
||||
Span<byte> checksum = new byte[20 + 0x10];
|
||||
ref var aesHdr = ref AsRef<KIRKEngine.KIRK_AES128CBC_HEADER>(checksum);
|
||||
aesHdr.mode = KIRKEngine.KIRK_MODE_ENCRYPT_CBC;
|
||||
aesHdr.keyseed = 0x63;
|
||||
aesHdr.data_size = 0x10;
|
||||
mac.CopyTo(checksum.Slice(20));
|
||||
KIRKEngine.sceUtilsBufferCopyWithRange(checksum, 0x10, checksum, 0x10,
|
||||
KIRKEngine.KIRK_CMD_ENCRYPT_IV_0);
|
||||
checksum.Slice(20, 0x10).CopyTo(mac);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,43 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33516.290
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PspCrypto", "PspCrypto\PspCrypto.csproj", "{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PbpResign", "PbpResign\PbpResign.csproj", "{834B9F97-1B63-48EB-922E-0FC155BB2481}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PsvImage", "PsvImage\PsvImage.csproj", "{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PopsBuilder", "PopsBuilder\PopsBuilder.csproj", "{D1DF66DB-52BE-489C-A292-525BC71F2BB2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {CEC62870-B8D5-4E33-9144-7C2B88EE8F73}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
static class AesHelper
|
||||
{
|
||||
public static byte[] CbcEncrypt(byte[] plainText, byte[] iv, byte[] key, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = plainText.Length;
|
||||
}
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
using var encrypt = aes.CreateEncryptor();
|
||||
var cipherText = encrypt.TransformFinalBlock(plainText, 0, size);
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
public static byte[] AesEcbDecrypt(byte[] cipherText, byte[] key, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = cipherText.Length;
|
||||
}
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.Key = key;
|
||||
using var decrypt = aes.CreateDecryptor();
|
||||
var plainText = decrypt.TransformFinalBlock(cipherText, 0, size);
|
||||
return plainText;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
public class CmaKeys
|
||||
{
|
||||
private static readonly byte[] Passphrase = Encoding.ASCII.GetBytes("Sri Jayewardenepura Kotte");
|
||||
private static readonly byte[] Key = { 0xA9, 0xFA, 0x5A, 0x62, 0x79, 0x9F, 0xCC, 0x4C, 0x72, 0x6B, 0x4E, 0x2C, 0xE3, 0x50, 0x6D, 0x38 };
|
||||
|
||||
public static byte[] GenerateKey(string aid)
|
||||
{
|
||||
var longlong = Convert.ToUInt64(aid, 16);
|
||||
var aidBytes = BitConverter.GetBytes(longlong);
|
||||
Array.Reverse(aidBytes);
|
||||
return GenerateKey(aidBytes);
|
||||
}
|
||||
|
||||
public static byte[] GenerateKey(byte[] aid)
|
||||
{
|
||||
var derviedKey = aid.Concat(Passphrase).ToArray();
|
||||
|
||||
using var sha = SHA256.Create();
|
||||
derviedKey = sha.ComputeHash(derviedKey);
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.Key = Key;
|
||||
using var decryptor = aes.CreateDecryptor();
|
||||
derviedKey = decryptor.TransformFinalBlock(derviedKey, 0, derviedKey.Length);
|
||||
return derviedKey;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
class PSVIMGBuilder
|
||||
{
|
||||
private byte[] iv;
|
||||
private byte[] key;
|
||||
private Stream mainStream;
|
||||
private SHA256 sha256;
|
||||
private Memory<byte> blockData;
|
||||
private int blockPosition;
|
||||
private long contentSize = 0;
|
||||
|
||||
private int blocksWritten = 0;
|
||||
private bool finished = false;
|
||||
|
||||
public long ContentSize => contentSize;
|
||||
|
||||
public int BlocksWritten => blocksWritten;
|
||||
|
||||
public bool HasFinished => finished;
|
||||
|
||||
//Footer
|
||||
private long totalBytes = 0;
|
||||
|
||||
public PSVIMGBuilder(Stream dst, byte[] Key)
|
||||
{
|
||||
|
||||
totalBytes = 0;
|
||||
contentSize = 0;
|
||||
sha256 = SHA256.Create();
|
||||
mainStream = dst;
|
||||
key = Key;
|
||||
|
||||
RandomNumberGenerator.Fill(iv);
|
||||
iv = AesHelper.AesEcbDecrypt(iv, key);
|
||||
|
||||
mainStream.Write(iv.AsSpan());
|
||||
totalBytes += iv.Length;
|
||||
|
||||
startNewBlock();
|
||||
}
|
||||
|
||||
public void AddFile(string FilePath, string ParentPath, string PathRel)
|
||||
{
|
||||
var sz = Utils.ToSceIoStat(FilePath).Size;
|
||||
}
|
||||
|
||||
private void startNewBlock()
|
||||
{
|
||||
blockData = new byte[PSVIMGConstants.FULL_PSVIMG_SIZE];
|
||||
blockPosition = 0;
|
||||
}
|
||||
|
||||
private byte[] shaBlock(int length = PSVIMGConstants.PSVIMG_BLOCK_SIZE)
|
||||
{
|
||||
return sha256.ComputeHash(blockData.ToArray(), 0, length);
|
||||
}
|
||||
|
||||
private void finishBlock(bool final = false)
|
||||
{
|
||||
int len = blockPosition;
|
||||
var shaBytes = shaBlock(len);
|
||||
shaBytes.CopyTo(blockData.Slice(blockPosition));
|
||||
len += PSVIMGConstants.SHA256_BLOCK_SIZE;
|
||||
|
||||
//Get next IV
|
||||
var encryptedBlock = AesHelper.CbcEncrypt(blockData.ToArray(), iv, key, len);
|
||||
for (int i = 0; i < iv.Length; i++)
|
||||
{
|
||||
int encBlockOffset = (encryptedBlock.Length - iv.Length) + i;
|
||||
iv[i] = encryptedBlock[encBlockOffset];
|
||||
}
|
||||
|
||||
mainStream.Write(encryptedBlock.AsSpan());
|
||||
totalBytes += encryptedBlock.Length;
|
||||
}
|
||||
|
||||
private int remainingBlockSize()
|
||||
{
|
||||
return (int)(PSVIMGConstants.PSVIMG_BLOCK_SIZE - blockPosition);
|
||||
}
|
||||
|
||||
private void writeBlock(Span<byte> date, bool update = false)
|
||||
{
|
||||
var dLen = date.Length;
|
||||
var writeTotal = 0;
|
||||
while (dLen > 0)
|
||||
{
|
||||
int remaining = remainingBlockSize();
|
||||
|
||||
if (dLen > remaining)
|
||||
{
|
||||
date.Slice(writeTotal, remaining).CopyTo(blockData.Span.Slice(blockPosition));
|
||||
blockPosition += remaining;
|
||||
writeTotal += remaining;
|
||||
dLen -= remaining;
|
||||
|
||||
finishBlock();
|
||||
startNewBlock();
|
||||
if (update)
|
||||
{
|
||||
blocksWritten += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
class PsvImgStream : Stream
|
||||
{
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanRead { get; }
|
||||
public override bool CanSeek { get; }
|
||||
public override bool CanWrite { get; }
|
||||
public override long Length { get; }
|
||||
public override long Position { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
internal class PSVIMGConstants
|
||||
{
|
||||
public const int AES_BLOCK_SIZE = 0x10;
|
||||
public const int SHA256_BLOCK_SIZE = 0x20;
|
||||
public const int PSVIMG_BLOCK_SIZE = 0x8000;
|
||||
public const int PSVIMG_ENTRY_ALIGN = 0x400;
|
||||
|
||||
public const string PSVIMG_HEADER_END = "EndOfHeader\n";
|
||||
public const string PSVIMG_TAILOR_END = "EndOfTailer\n";
|
||||
public const string PSVIMG_PADDING_END = "\n";
|
||||
|
||||
public const int FULL_PSVIMG_SIZE = PSVIMG_BLOCK_SIZE + SHA256_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SceDateTime
|
||||
{
|
||||
public ushort Year;
|
||||
public ushort Month;
|
||||
public ushort Day;
|
||||
public ushort Hour;
|
||||
public ushort Minute;
|
||||
public ushort Second;
|
||||
public uint Microsecond;
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct SceIoStat
|
||||
{
|
||||
[Flags]
|
||||
public enum Modes
|
||||
{
|
||||
/** Format bits mask */
|
||||
FormatBits = 0xF000,
|
||||
/** Symbolic link */
|
||||
SymbLink = 0x4000,
|
||||
/** Directory */
|
||||
Directory = 0x1000,
|
||||
/** Regular file */
|
||||
File = 0x2000,
|
||||
|
||||
/** Set UID */
|
||||
SetUid = 0x0800,
|
||||
/** Set GID */
|
||||
SetGid = 0x0400,
|
||||
/** Sticky */
|
||||
Sticky = 0x0200,
|
||||
|
||||
/** Others access rights mask */
|
||||
OthersAcesssMask = 0x01C0,
|
||||
/** Others read permission */
|
||||
OthersRead = 0x0100,
|
||||
/** Others write permission */
|
||||
OthersWrite = 0x0080,
|
||||
/** Others execute permission */
|
||||
OthersExecute = 0x0040,
|
||||
|
||||
/** Group access rights mask */
|
||||
GroupAcessMask = 0x0038,
|
||||
/** Group read permission */
|
||||
GroupRead = 0x0020,
|
||||
/** Group write permission */
|
||||
GroupWrite = 0x0010,
|
||||
/** Group execute permission */
|
||||
GroupExecute = 0x0008,
|
||||
|
||||
/** User access rights mask */
|
||||
UserAcessMask = 0x0007,
|
||||
/** User read permission */
|
||||
UserRead = 0x0004,
|
||||
/** User write permission */
|
||||
UserWrite = 0x0002,
|
||||
/** User execute permission */
|
||||
UserExecute = 0x0001,
|
||||
};
|
||||
public enum AttributesEnum
|
||||
{
|
||||
/** Format mask */
|
||||
FormatMask = 0x0038, // Format mask
|
||||
/** Symlink */
|
||||
SymbLink = 0x0008, // Symbolic link
|
||||
/** Directory */
|
||||
Directory = 0x0010, // Directory
|
||||
/** Regular file */
|
||||
File = 0x0020, // Regular file
|
||||
|
||||
/** Hidden read permission */
|
||||
Read = 0x0004, // read
|
||||
/** Hidden write permission */
|
||||
Write = 0x0002, // write
|
||||
/** Hidden execute permission */
|
||||
Execute = 0x0001, // execute
|
||||
};
|
||||
|
||||
public Modes Mode;
|
||||
public AttributesEnum Attributes;
|
||||
/** Size of the file in bytes. */
|
||||
public UInt64 Size;
|
||||
/** Creation time. */
|
||||
public SceDateTime CreationTime;
|
||||
/** Access time. */
|
||||
public SceDateTime AccessTime;
|
||||
/** Modification time. */
|
||||
public SceDateTime ModificaionTime;
|
||||
/** Device-specific data. */
|
||||
public fixed uint Private[6];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct PsvImgTailer
|
||||
{
|
||||
|
||||
public ulong Flags;
|
||||
public fixed byte Padding[1004];
|
||||
public fixed byte bEnd[12];
|
||||
}
|
||||
|
||||
internal class PSVIMGPadding
|
||||
{
|
||||
public static long GetPadding(long size)
|
||||
{
|
||||
long padding;
|
||||
if ((size & (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1)) >= 1)
|
||||
{
|
||||
padding = (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - (size & (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
padding = 0;
|
||||
}
|
||||
return padding;
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe struct PsvImgHeader
|
||||
{
|
||||
public ulong SysTime;
|
||||
public ulong Flags;
|
||||
public SceIoStat Statistics;
|
||||
public fixed byte bParentPath[256];
|
||||
public uint unk_16C;
|
||||
public fixed byte bPath[256];
|
||||
public fixed byte Padding[904];
|
||||
public fixed byte bEnd[12];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
class PsvmdBuilder
|
||||
{
|
||||
|
||||
public static void CreatePsvmd(Stream OutputStream, Stream EncryptedPsvimg, long ContentSize, string BackupType,
|
||||
byte[] Key)
|
||||
{
|
||||
Span<byte> iv = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
|
||||
EncryptedPsvimg.Seek(0, SeekOrigin.Begin);
|
||||
EncryptedPsvimg.Read(iv);
|
||||
iv = AesHelper.AesEcbDecrypt(iv.ToArray(), Key);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
public static SceDateTime ToSceDateTime(this DateTime dateTime)
|
||||
{
|
||||
var sceDateTime = new SceDateTime();
|
||||
sceDateTime.Year = (ushort)dateTime.Year;
|
||||
sceDateTime.Month = (ushort)dateTime.Month;
|
||||
sceDateTime.Day = (ushort)dateTime.Day;
|
||||
sceDateTime.Hour = (ushort)dateTime.Hour;
|
||||
sceDateTime.Minute = (ushort)dateTime.Minute;
|
||||
sceDateTime.Second = (ushort)dateTime.Second;
|
||||
sceDateTime.Microsecond = (uint)dateTime.Millisecond * 1000;
|
||||
return sceDateTime;
|
||||
}
|
||||
|
||||
public static SceIoStat ToSceIoStat(string path)
|
||||
{
|
||||
var stats = new SceIoStat();
|
||||
var attributes = File.GetAttributes(path);
|
||||
|
||||
if (attributes.HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.Directory;
|
||||
stats.Size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.File;
|
||||
stats.Size = (ulong)(new FileInfo(path).Length);
|
||||
}
|
||||
|
||||
if (attributes.HasFlag(FileAttributes.ReadOnly))
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.GroupRead;
|
||||
stats.Mode |= SceIoStat.Modes.OthersRead;
|
||||
stats.Mode |= SceIoStat.Modes.UserRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.GroupRead;
|
||||
stats.Mode |= SceIoStat.Modes.GroupWrite;
|
||||
stats.Mode |= SceIoStat.Modes.OthersRead;
|
||||
stats.Mode |= SceIoStat.Modes.OthersWrite;
|
||||
stats.Mode |= SceIoStat.Modes.UserRead;
|
||||
stats.Mode |= SceIoStat.Modes.UserWrite;
|
||||
}
|
||||
|
||||
stats.CreationTime = File.GetCreationTimeUtc(path).ToSceDateTime();
|
||||
stats.AccessTime = File.GetLastAccessTimeUtc(path).ToSceDateTime();
|
||||
stats.ModificaionTime = File.GetLastWriteTimeUtc(path).ToSceDateTime();
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue