Initial commit

This commit is contained in:
Li 2023-04-14 15:55:11 +12:00
parent 04bafb8b70
commit 7b6830a40b
83 changed files with 17132 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -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/*

View File

@ -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>

1371
PbpResign/Program.cs Normal file

File diff suppressed because it is too large Load Diff

78
PbpResign/Sfo.cs Normal file
View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

107
PopsBuilder/Cue/CueIndex.cs Normal file
View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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.");
}
}
}

105
PopsBuilder/Pops/PopsImg.cs Normal file
View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}
}

103
PopsBuilder/Resources.Designer.cs generated Normal file
View File

@ -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));
}
}
}
}

133
PopsBuilder/Resources.resx Normal file
View File

@ -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.

116
PopsBuilder/StreamUtil.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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;
}
}
}
}

710
PspCrypto/AMCTRL.cs Normal file
View File

@ -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;
}
}
}

250
PspCrypto/AesHelper.cs Normal file
View File

@ -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);
}
}
}

126
PspCrypto/AtracCrypto.cs Normal file
View File

@ -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
}
}
}
}

172
PspCrypto/DNASHelper.cs Normal file
View File

@ -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;
}
}
}

636
PspCrypto/DNASStream.cs Normal file
View File

@ -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;
}
}
}

127
PspCrypto/ECDsaHelper.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

1113
PspCrypto/KIRKEngine.cs Normal file

File diff suppressed because it is too large Load Diff

231
PspCrypto/KeyVault.cs Normal file
View File

@ -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 };
}
}

8
PspCrypto/Libraries.cs Normal file
View File

@ -0,0 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text;
internal static class Libraries
{
internal const string CryptoNative = "PspCryptoHelper";
}

53
PspCrypto/Lz.cs Normal file
View File

@ -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;
}
}
}

836
PspCrypto/Lzrc.cs Normal file
View File

@ -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];
}
}
}
}

View File

@ -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>

12
PspCrypto/PspParameter.cs Normal file
View File

@ -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; }
}
}

73
PspCrypto/Resource.Designer.cs generated Normal file
View File

@ -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));
}
}
}
}

124
PspCrypto/Resource.resx Normal file
View File

@ -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>

38
PspCrypto/RijndaelMod.cs Normal file
View File

@ -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

View File

@ -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
};
}
}

View File

@ -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; }
}
}
}

View File

@ -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;
}
}
}

28
PspCrypto/SceDdrdb.cs Normal file
View File

@ -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);
}
}
}

513
PspCrypto/SceMemlmd.cs Normal file
View File

@ -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;
}
}
}

2662
PspCrypto/SceMesgLed.cs Normal file

File diff suppressed because it is too large Load Diff

1286
PspCrypto/SceNpDrm.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}
}

264
PspCrypto/Structs.cs Normal file
View File

@ -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;
}
}

85
PspCrypto/Utils.cs Normal file
View File

@ -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);
}
}
}

BIN
PspCrypto/__sce_discinfo Normal file

Binary file not shown.

BIN
PspCryptoHelper.dll Normal file

Binary file not shown.

43
PspTest.sln Normal file
View File

@ -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

42
PsvImage/AesHelper.cs Normal file
View File

@ -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;
}
}
}

38
PsvImage/CmaKeys.cs Normal file
View File

@ -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;
}
}
}

119
PsvImage/PSVIMGBuilder.cs Normal file
View File

@ -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
{
}
}
}
}
}

8
PsvImage/PsvImage.csproj Normal file
View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

43
PsvImage/PsvImgStream.cs Normal file
View File

@ -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; }
}
}

154
PsvImage/PsvImgStructs.cs Normal file
View File

@ -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];
}
}

22
PsvImage/PsvmdBuilder.cs Normal file
View File

@ -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);
}
}
}

62
PsvImage/Utils.cs Normal file
View File

@ -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;
}
}
}