1339 lines
45 KiB
C#
1339 lines
45 KiB
C#
using PspCrypto.Security.Cryptography;
|
|
using System;
|
|
using System.Buffers.Binary;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace PspCrypto
|
|
{
|
|
public static class SceNpDrm
|
|
{
|
|
unsafe struct SceEbootPbp
|
|
{
|
|
public ulong Magic;
|
|
public int KeyType;
|
|
public int Type;
|
|
private fixed byte _contentId[0x30];
|
|
|
|
public Span<byte> ContentId
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _contentId)
|
|
{
|
|
return new Span<byte>(ptr, 0x30);
|
|
}
|
|
}
|
|
}
|
|
|
|
public ulong Aid;
|
|
public ulong SecureTick;
|
|
public long PbpSize;
|
|
public int SwVer;
|
|
public int DiscCount;
|
|
private fixed long _discOffsets[6];
|
|
|
|
public Span<long> DiscOffsets
|
|
{
|
|
get
|
|
{
|
|
fixed (long* ptr = _discOffsets)
|
|
{
|
|
return new Span<long>(ptr, 6);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _padding[0xC8];
|
|
private fixed byte _pbpHdrSig[0x38];
|
|
|
|
public Span<byte> PbpHdrSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _pbpHdrSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _npUmdImgSig[0x38];
|
|
|
|
public Span<byte> NpUmdImgSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _npUmdImgSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _sig[0x38];
|
|
|
|
public Span<byte> Sig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _sig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unsafe struct SceEbootPbp100
|
|
{
|
|
public ulong Magic;
|
|
public int KeyType;
|
|
public int Type;
|
|
private fixed byte _contentId[0x30];
|
|
|
|
public Span<byte> ContentId
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _contentId)
|
|
{
|
|
return new Span<byte>(ptr, 0x30);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _padding[0x18];
|
|
private fixed byte _pbpHdrSig[0x38];
|
|
|
|
public Span<byte> PbpHdrSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _pbpHdrSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _npUmdImgSig[0x38];
|
|
|
|
public Span<byte> NpUmdImgSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _npUmdImgSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _sig[0x38];
|
|
|
|
public Span<byte> Sig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _sig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe struct SceDiscInfo
|
|
{
|
|
private fixed byte _id[0x30];
|
|
|
|
public Span<byte> Id
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _id)
|
|
{
|
|
return new Span<byte>(ptr, 0x30);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int Version;
|
|
public int DiscCount;
|
|
public long FileSize;
|
|
private fixed long _diskOffsets[6];
|
|
|
|
public Span<long> Offsets
|
|
{
|
|
get
|
|
{
|
|
fixed (long* ptr = _diskOffsets)
|
|
{
|
|
return new Span<long>(ptr, 6);
|
|
}
|
|
}
|
|
}
|
|
|
|
private fixed byte _pad[0x20];
|
|
private fixed byte _discsSig[0x38];
|
|
|
|
public Span<byte> DiscsSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _discsSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
private fixed byte _sig[0x38];
|
|
|
|
public Span<byte> Sig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _sig)
|
|
{
|
|
return new Span<byte>(ptr, 0x38);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public static ulong Aid { get; set; }
|
|
|
|
public static byte[] SceDiskInfo => Resource.__sce_discinfo;
|
|
|
|
private const int BLK_SIZE = 0x7C0;
|
|
|
|
private static readonly Memory<byte> _memory = new byte[0x1000];
|
|
|
|
private static byte[] _psId;
|
|
|
|
private static long _fuseId;
|
|
|
|
public static void SetPSID(byte[] psid)
|
|
{
|
|
_psId = psid;
|
|
}
|
|
|
|
public static void SetFuseId(long fuseId)
|
|
{
|
|
_fuseId = fuseId;
|
|
}
|
|
|
|
public static void SetFuseId(Span<byte> fuseId)
|
|
{
|
|
SetFuseId(MemoryMarshal.Read<long>(fuseId));
|
|
}
|
|
|
|
public unsafe struct SceNpDrmKey
|
|
{
|
|
private fixed byte _KeyData[16];
|
|
public Span<byte> KeyData
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _KeyData)
|
|
{
|
|
return new Span<byte>(ptr, 16);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public unsafe struct Rif
|
|
{
|
|
private short _version;
|
|
private short _versionFlag;
|
|
private int _drmType;
|
|
private fixed byte _accountId[8];
|
|
private fixed byte _contentId[0x30];
|
|
private fixed byte _encKey1[0x10];
|
|
private fixed byte _encKey2[0x10];
|
|
private long _startTime;
|
|
private long _endTime;
|
|
private fixed byte _ecdsaSig[0x28];
|
|
public short Version => _version;
|
|
public short VersionFlag => _versionFlag;
|
|
public int DrmType => _drmType;
|
|
public Span<byte> AccountId
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _accountId)
|
|
{
|
|
return new Span<byte>(ptr, 8);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> ContentId
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _contentId)
|
|
{
|
|
return new Span<byte>(ptr, 0x30);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> EncKey1
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _encKey1)
|
|
{
|
|
return new Span<byte>(ptr, 0x10);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> EncKey2
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _encKey2)
|
|
{
|
|
return new Span<byte>(ptr, 0x10);
|
|
}
|
|
}
|
|
}
|
|
public long StartTime => _startTime;
|
|
public long EndTime => _endTime;
|
|
|
|
public Span<byte> EcdsaSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _ecdsaSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x28);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public unsafe struct Act
|
|
{
|
|
private short _actType;
|
|
private short _versionFlag;
|
|
private int _version;
|
|
private fixed byte _accountId[8];
|
|
private fixed byte _primKeyTable[0x800];
|
|
private fixed byte _unk1[0x40];
|
|
private fixed byte _openPsId[0x10];
|
|
private fixed byte _unk2[0x10];
|
|
private fixed byte _unk4[0x10];
|
|
private fixed byte _secondTable[0x650];
|
|
private fixed byte _rsaSig[0x100];
|
|
private fixed byte _unkSig[0x40];
|
|
private fixed byte _ecdsaSig[0x28];
|
|
public short ActType => _actType;
|
|
public short VersionFlag => _versionFlag;
|
|
public int Version => _version;
|
|
public Span<byte> AccountId
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _accountId)
|
|
{
|
|
return new Span<byte>(ptr, 8);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> PrimKeyTable
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _primKeyTable)
|
|
{
|
|
return new Span<byte>(ptr, 0x800);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> Unk1
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _unk1)
|
|
{
|
|
return new Span<byte>(ptr, 0x40);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> OpenPsId
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _openPsId)
|
|
{
|
|
return new Span<byte>(ptr, 0x10);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> Unk2
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _unk2)
|
|
{
|
|
return new Span<byte>(ptr, 0x10);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> Unk4
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _unk4)
|
|
{
|
|
return new Span<byte>(ptr, 0x10);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> SecondTable
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _secondTable)
|
|
{
|
|
return new Span<byte>(ptr, 0x650);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> RsaSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _rsaSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x100);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> UnkSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _unkSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x40);
|
|
}
|
|
}
|
|
}
|
|
public Span<byte> EcdsaSig
|
|
{
|
|
get
|
|
{
|
|
fixed (byte* ptr = _ecdsaSig)
|
|
{
|
|
return new Span<byte>(ptr, 0x28);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
public static int sceNpDrmTransformVersionKey(Span<byte> versionKey, int srcKeyType, int dstKeyType)
|
|
{
|
|
// reverse the previous "GenVersionKey" step ..
|
|
int ret = ReverseGenVersionKey(versionKey, srcKeyType);
|
|
if (ret >= 0)
|
|
ret = GenVersionKey(versionKey, dstKeyType); // generate a new version key of this type
|
|
return ret;
|
|
}
|
|
|
|
public static int sceNpDrmGetIDps(Span<byte> idps)
|
|
{
|
|
if (_psId != null && idps != null)
|
|
{
|
|
_psId.CopyTo(idps);
|
|
return 0;
|
|
}
|
|
return -0x7faaf6ff;
|
|
}
|
|
|
|
public static int GetActKey(Span<byte> key, ReadOnlySpan<byte> keyTable, int count)
|
|
{
|
|
Span<byte> idps = stackalloc byte[16];
|
|
Span<byte> decKey = stackalloc byte[16];
|
|
int ret = sceNpDrmGetIDps(idps);
|
|
if (ret > -1)
|
|
{
|
|
ret = AesHelper.AesEncrypt(KeyVault.drmActdatKey, decKey, idps);
|
|
if (ret == 16)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ret = AesHelper.AesDecrypt(keyTable[(i * 0x10)..], key[(i * 0x10)..], decKey);
|
|
if (ret != 16)
|
|
{
|
|
ret = unchecked((int)0x80550902);
|
|
break;
|
|
}
|
|
}
|
|
ret = 0;
|
|
}
|
|
}
|
|
idps.Clear();
|
|
decKey.Clear();
|
|
return ret;
|
|
}
|
|
|
|
private static int GetFreeVersionKey(Span<byte> versionKey, ReadOnlySpan<byte> rifBuf)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
private static int GetLicenseVersionKey(Span<byte> versionKey, ReadOnlySpan<byte> actBuf, ReadOnlySpan<byte> rifBuf)
|
|
{
|
|
Span<byte> buf1 = stackalloc byte[16];
|
|
Span<byte> buf2 = stackalloc byte[16];
|
|
int ret = sceNpDrmVerifyAct(actBuf);
|
|
if (ret > -1)
|
|
{
|
|
ret = sceNpDrmVerifyRif(rifBuf);
|
|
if (ret > -1)
|
|
{
|
|
var act = MemoryMarshal.AsRef<Act>(actBuf);
|
|
var rif = MemoryMarshal.AsRef<Rif>(rifBuf);
|
|
if (!act.AccountId.SequenceEqual(rif.AccountId))
|
|
{
|
|
return -0x7faaf6fb;
|
|
}
|
|
if (((act.ActType & 0xff) << 8 | act.ActType >> 8) < ((rif.Version & 0xff) << 8 | rif.Version >> 9))
|
|
{
|
|
return -0x7faaf6ef;
|
|
}
|
|
AesHelper.AesDecrypt(rif.EncKey1, buf1, KeyVault.drmRifKey);
|
|
int keyIdx = MemoryMarshal.Read<int>(buf1[12..]);
|
|
keyIdx >>= 0x18;
|
|
if (keyIdx >= 0x80)
|
|
{
|
|
return -0x7faaf6fe;
|
|
}
|
|
ret = GetActKey(buf2, act.PrimKeyTable[(keyIdx * 0x10)..], 1);
|
|
if (ret > -1)
|
|
{
|
|
ret = AesHelper.AesDecrypt(rif.EncKey2, versionKey, buf2);
|
|
if (ret != 16)
|
|
{
|
|
ret = -0x7faaf6fe;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
buf1.Clear();
|
|
buf2.Clear();
|
|
return ret;
|
|
}
|
|
|
|
private static int CheckRifExpress(ReadOnlySpan<byte> rifBuf)
|
|
{
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
|
|
private static int ReverseGenVersionKey(Span<byte> versionkey, int type)
|
|
{
|
|
type &= 0xffffff;
|
|
int ret = 0;
|
|
if (type != 0)
|
|
{
|
|
ret = unchecked((int)0x80550901);
|
|
if (type < 4)
|
|
{
|
|
ret = AesHelper.AesDecrypt(versionkey, versionkey, KeyVault.drmVersionKeyKey.AsSpan().Slice(type * 0x10, 0x10));
|
|
ret = ret == 16 ? 0 : unchecked((int)0x80550902);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
private static int GenVersionKey(Span<byte> versionKey, int type)
|
|
{
|
|
type &= 0xffffff;
|
|
int ret = 0;
|
|
if (type != 0)
|
|
{
|
|
ret = unchecked((int)0x80550901);
|
|
if (type < 4)
|
|
{
|
|
ret = AesHelper.AesEncrypt(versionKey, versionKey, KeyVault.drmVersionKeyKey.AsSpan().Slice(type * 0x10, 0x10));
|
|
ret = ret == 16 ? 0 : unchecked((int)0x80550902);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static int sceNpDrmGetVersionKey(Span<byte> versionKey, ReadOnlySpan<byte> actBuf, ReadOnlySpan<byte> rifBuf, int type)
|
|
{
|
|
int ret = -0x7faaf6ff;
|
|
if (versionKey != null && rifBuf != null)
|
|
{
|
|
var rif = MemoryMarshal.AsRef<Rif>(rifBuf);
|
|
if (rif.DrmType == 0x300000)
|
|
{
|
|
ret = GetFreeVersionKey(versionKey, rifBuf);
|
|
}
|
|
else
|
|
{
|
|
if (actBuf == null)
|
|
{
|
|
return ret;
|
|
}
|
|
ret = GetLicenseVersionKey(versionKey, actBuf, rifBuf);
|
|
}
|
|
if (ret > -1)
|
|
{
|
|
ret = CheckRifExpress(rifBuf);
|
|
if (ret > -1)
|
|
{
|
|
ret = GenVersionKey(versionKey, type);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static int sceNpDrmGetFixedKey(Span<byte> fixedKey, ReadOnlySpan<byte> contentId, int type)
|
|
{
|
|
Span<byte> buf = stackalloc byte[0x30];
|
|
Span<byte> mkey = stackalloc byte[48];
|
|
int ret = -0x7faaf6ff;
|
|
if ((type & 0x1000000) != 0)
|
|
{
|
|
contentId[..0x30].CopyTo(buf);
|
|
ret = AMCTRL.sceDrmBBMacInit(mkey, 1);
|
|
if (ret == 0)
|
|
{
|
|
ret = AMCTRL.sceDrmBBMacUpdate(mkey, buf, 0x30);
|
|
if (ret == 0)
|
|
{
|
|
ret = AMCTRL.sceDrmBBMacFinal(mkey, fixedKey, KeyVault.DrmFixedKey);
|
|
if (ret == 0)
|
|
{
|
|
ret = GenVersionKey(fixedKey, (int)(type & 0xfeffffff));
|
|
}
|
|
else
|
|
{
|
|
ret = -0x7faaf6fe;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static int sceNpDrmGetContentKey(Span<byte> contentKey, ReadOnlySpan<byte> actBuf, ReadOnlySpan<byte> rifBuf) => sceNpDrmGetVersionKey(contentKey, actBuf, rifBuf, 0);
|
|
|
|
public static int sceNpDrmVerifyAct(ReadOnlySpan<byte> actBuf)
|
|
{
|
|
int ret = -0x7faaf6ff;
|
|
if (actBuf != null)
|
|
{
|
|
ret = -0x7faaf6fa;
|
|
var act = MemoryMarshal.AsRef<Act>(actBuf);
|
|
if (((act.VersionFlag & 0xFF) << 8 | act.VersionFlag >> 8) < 2)
|
|
{
|
|
if (((act.ActType & 0xFF) << 8 | act.ActType >> 8) < 2)
|
|
{
|
|
ret = VerifySig(act.EcdsaSig, KeyVault.drmActRifSig, actBuf, 0x1010);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
private static int VerifySig(ReadOnlySpan<byte> sig, ReadOnlySpan<byte> pubKey, ReadOnlySpan<byte> data, int dataSize)
|
|
{
|
|
int ret;
|
|
Span<byte> digest = stackalloc byte[32];
|
|
if (dataSize < 0x801)
|
|
{
|
|
ret = SceDdrdb.sceDdrdbHash(data, dataSize, digest);
|
|
}
|
|
else
|
|
{
|
|
ret = (SHA1.HashData(data[..dataSize], digest) > 0 ? 0 : -1);
|
|
}
|
|
if (ret > -1)
|
|
{
|
|
ret = SceDdrdb.sceDdrdbSigvry(pubKey, digest, sig);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
private static int FillFuseId(Span<uint> fuseId)
|
|
{
|
|
if (_fuseId != 0)
|
|
{
|
|
uint heigh = (uint)((_fuseId >> 32) & 0xffffffff);
|
|
uint low = (uint)(_fuseId & 0xffffffff);
|
|
fuseId[0] = BinaryPrimitives.ReverseEndianness(heigh);
|
|
fuseId[1] = BinaryPrimitives.ReverseEndianness(low);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static int VerifyRif(ReadOnlySpan<byte> rifBuf)
|
|
{
|
|
int ret = -0x7faaf6ff;
|
|
if (rifBuf != null)
|
|
{
|
|
ret = -0x7faaf6fa;
|
|
var rif = MemoryMarshal.AsRef<Rif>(rifBuf);
|
|
if (((rif.VersionFlag & 0xff) << 8 | rif.VersionFlag >> 8) < 3)
|
|
{
|
|
if (((rif.Version & 0xff) << 8 | rif.Version >> 8) < 2)
|
|
{
|
|
int ret2;
|
|
if ((rif.DrmType & 0x10000) == 0)
|
|
{
|
|
ret = VerifySig(rif.EcdsaSig, KeyVault.drmActRifSig, rifBuf, 0x70);
|
|
ret2 = -0x7faaf6fc;
|
|
}
|
|
else
|
|
{
|
|
Span<byte> buffer = stackalloc byte[0x90];
|
|
rifBuf[..0x70].CopyTo(buffer);
|
|
if (_psId == null)
|
|
{
|
|
return ret;
|
|
}
|
|
ret = sceNpDrmGetIDps(buffer[0x70..]);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
Span<uint> fuseId = MemoryMarshal.Cast<byte, uint>(buffer[0x80..]);
|
|
ret = FillFuseId(fuseId);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ret = VerifySig(rif.EcdsaSig, KeyVault.drmActRifSig, buffer, 0x90);
|
|
ret2 = -0x7faaf6e8;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
ret = ret2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static int sceNpDrmVerifyRif(ReadOnlySpan<byte> rifBuf)
|
|
{
|
|
int ret = VerifyRif(rifBuf);
|
|
if (ret > -1)
|
|
{
|
|
var rif = MemoryMarshal.AsRef<Rif>(rifBuf);
|
|
if (rif.DrmType == 0x3000000)
|
|
{
|
|
ret = -1; // TODO
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static int KsceNpDrmEbootSigGenMultiDisc(string fileName, ReadOnlySpan<byte> sceDiscInfo,
|
|
Span<byte> ebootSig, int swVer)
|
|
{
|
|
return SceNpDrmEbootSigGenMultiDisc(fileName, sceDiscInfo, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
|
}
|
|
public static int KsceNpDrmEbootSigGenPs1(Stream file, Span<byte> ebootSig, int swVer)
|
|
{
|
|
return SceNpDrmEbootSigGen(file, 3, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
|
}
|
|
|
|
public static int KsceNpDrmEbootSigGenPsp(Stream file, Span<byte> ebootSig, int swVer)
|
|
{
|
|
return SceNpDrmEbootSigGen(file, 2, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
|
}
|
|
|
|
public static int KsceNpDrmPspEbootSigGen(Stream file, Span<byte> ebootSig)
|
|
{
|
|
return SceNpDrmPspEbootSigGen(file, ebootSig, _memory.Span, BLK_SIZE);
|
|
}
|
|
|
|
public static int KsceNpDrmEbootSigGenPs1(string fileName, Span<byte> ebootSig, int swVer)
|
|
{
|
|
return SceNpDrmEbootSigGen(fileName, 3, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
|
}
|
|
|
|
public static int KsceNpDrmEbootSigGenPsp(string fileName, Span<byte> ebootSig, int swVer)
|
|
{
|
|
return SceNpDrmEbootSigGen(fileName, 2, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
|
}
|
|
|
|
public static int KsceNpDrmPspEbootSigGen(string fileName, Span<byte> ebootSig)
|
|
{
|
|
return SceNpDrmPspEbootSigGen(fileName, ebootSig, _memory.Span, BLK_SIZE);
|
|
}
|
|
|
|
private static int SceNpDrmEbootSigGenMultiDisc(string fileName, ReadOnlySpan<byte> sceDiskInfo,
|
|
Span<byte> ebootSig, int swVer, Span<byte> buffer, int blockSize)
|
|
{
|
|
Span<byte> pbpHdrDigest = stackalloc byte[32];
|
|
Span<byte> discsDigest = stackalloc byte[32];
|
|
Span<byte> ebootSigtmp = stackalloc byte[512];
|
|
ref var sceEbootPbp = ref MemoryMarshal.AsRef<SceEbootPbp>(ebootSigtmp);
|
|
if ((blockSize & 0x3F) != 0)
|
|
{
|
|
blockSize &= unchecked((int)0xFFFFFFC0);
|
|
}
|
|
|
|
if (sceDiskInfo == null || string.IsNullOrWhiteSpace(fileName) || buffer == null || ebootSig == null ||
|
|
blockSize < 0x400)
|
|
{
|
|
return unchecked((int)0x80870001);
|
|
}
|
|
|
|
Span<byte> secureTick = stackalloc byte[8] { 0xD4, 0x7A, 0x2C, 0x13, 0x64, 0x59, 0xE2, 0x00 };
|
|
//RandomNumberGenerator.Fill(secureTick);
|
|
|
|
ebootSig.Fill(0);
|
|
sceEbootPbp.SwVer = swVer;
|
|
sceEbootPbp.Aid = Aid;
|
|
sceEbootPbp.SecureTick = MemoryMarshal.AsRef<ulong>(secureTick);
|
|
|
|
var sha224 = SHA224.Create();
|
|
var hash = sha224.ComputeHash(sceDiskInfo[..200].ToArray());
|
|
var ret = SceSblGcAuthMgrDrmBBForDriver_4B506BE7(hash, sceDiskInfo[200..], 1000);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
var discInfo = MemoryMarshal.AsRef<SceDiscInfo>(sceDiskInfo);
|
|
ret = unchecked((int)0x80870005);
|
|
if (discInfo.DiscCount > 6)
|
|
{
|
|
return ret;
|
|
}
|
|
var fi = new FileInfo(fileName);
|
|
if (!fi.Exists)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (fi.Length != discInfo.FileSize)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = unchecked((int)0x80870001);
|
|
if (discInfo.DiscCount < 7)
|
|
{
|
|
if (fi.Length < discInfo.Offsets[0])
|
|
{
|
|
return ret;
|
|
}
|
|
if (discInfo.DiscCount > 1 && fi.Length < discInfo.Offsets[1])
|
|
{
|
|
return ret;
|
|
}
|
|
if (discInfo.DiscCount > 2 && fi.Length < discInfo.Offsets[2])
|
|
{
|
|
return ret;
|
|
}
|
|
if (discInfo.DiscCount > 3 && fi.Length < discInfo.Offsets[3])
|
|
{
|
|
return ret;
|
|
}
|
|
if (discInfo.DiscCount > 4 && fi.Length < discInfo.Offsets[4])
|
|
{
|
|
return ret;
|
|
}
|
|
if (discInfo.DiscCount > 5 && fi.Length < discInfo.Offsets[5])
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
using var stream = fi.OpenRead();
|
|
ret = SceMultiDiscDigest(stream, fi.Length, discInfo.DiscCount, discInfo.Offsets, pbpHdrDigest, discsDigest, buffer, blockSize);
|
|
if (discInfo.DiscCount >= 0)
|
|
{
|
|
stream.Close();
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_4B506BE7(discsDigest, discInfo.DiscsSig, 1000);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
sceEbootPbp.PbpSize = fi.Length;
|
|
sceEbootPbp.Magic = 0x47495349544C554D;
|
|
sceEbootPbp.KeyType = 1;
|
|
sceEbootPbp.Type = 4;
|
|
discInfo.Id.CopyTo(sceEbootPbp.ContentId);
|
|
sceEbootPbp.DiscCount = discInfo.DiscCount;
|
|
discInfo.Offsets.CopyTo(sceEbootPbp.DiscOffsets);
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(pbpHdrDigest, sceEbootPbp.PbpHdrSig, 1);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
discInfo.DiscsSig.CopyTo(sceEbootPbp.NpUmdImgSig);
|
|
var ebootsigDigst = sha224.ComputeHash(ebootSigtmp[..0x1C8].ToArray());
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(ebootsigDigst, sceEbootPbp.Sig, 1);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ebootSigtmp.CopyTo(ebootSig);
|
|
ret = 0;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
private static int SceMultiDiscDigest(Stream stream, long fileSize, int diskCount,
|
|
ReadOnlySpan<long> diskOffsets, Span<byte> pbpHdrDigest, Span<byte> dataDigest, Span<byte> buffer,
|
|
int blockSize)
|
|
{
|
|
var sha224 = SHA224.Create();
|
|
var ret = unchecked((int)0x80870005);
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
buffer = buffer[..blockSize];
|
|
var readSize = stream.Read(buffer);
|
|
if (readSize < 0x28)
|
|
{
|
|
return ret;
|
|
}
|
|
var pbpHeader = MemoryMarshal.AsRef<PbpHeader>(buffer);
|
|
if (pbpHeader.Sig != 0x50425000)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
var tmp = readSize;
|
|
if (pbpHeader.DataPsarOff < readSize)
|
|
{
|
|
tmp = pbpHeader.DataPsarOff;
|
|
}
|
|
|
|
var paramReadSize = pbpHeader.Icon0Off;
|
|
if (paramReadSize > 0x400)
|
|
{
|
|
paramReadSize = 0x400;
|
|
}
|
|
|
|
if (tmp < paramReadSize)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
sha224.TransformFinalBlock(buffer.ToArray(), 0, paramReadSize);
|
|
sha224.Hash.CopyTo(pbpHdrDigest);
|
|
|
|
sha224.Initialize();
|
|
sha224.TransformBlock(buffer.ToArray(), 0, paramReadSize, null, 0);
|
|
|
|
long end = pbpHeader.DataPsarOff + 0xC0000;
|
|
if (diskOffsets[0] >= pbpHeader.DataPsarOff && diskOffsets[0] < end)
|
|
{
|
|
end = diskOffsets[0];
|
|
}
|
|
|
|
long start = pbpHeader.DataPsarOff;
|
|
stream.Seek(start, SeekOrigin.Begin);
|
|
for (; start < end; start += readSize)
|
|
{
|
|
var toRead = (int)(end - start);
|
|
if (toRead > blockSize)
|
|
{
|
|
toRead = blockSize;
|
|
}
|
|
|
|
readSize = stream.Read(buffer[..toRead]);
|
|
if (readSize == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
sha224.TransformBlock(buffer.ToArray(), 0, readSize, null, 0);
|
|
}
|
|
|
|
if (diskCount != 0)
|
|
{
|
|
var start1 = diskOffsets[0];
|
|
end = diskOffsets[0] + 0xC0000;
|
|
if (end >= 0)
|
|
{
|
|
var discNo = 0;
|
|
var idx = 5;
|
|
while (true)
|
|
{
|
|
if (fileSize < end)
|
|
{
|
|
end = fileSize;
|
|
}
|
|
|
|
if (++discNo < diskCount)
|
|
{
|
|
start = diskOffsets[idx - 4];
|
|
if (start >= start1 && start < end)
|
|
{
|
|
end = start;
|
|
}
|
|
}
|
|
|
|
if (start1 < end)
|
|
{
|
|
stream.Seek(start1, SeekOrigin.Begin);
|
|
while (true)
|
|
{
|
|
var toRead = (int)(end - start1);
|
|
if (toRead > blockSize)
|
|
{
|
|
toRead = blockSize;
|
|
}
|
|
|
|
readSize = stream.Read(buffer[..toRead]);
|
|
if (readSize == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
sha224.TransformBlock(buffer.ToArray(), 0, readSize, null, 0);
|
|
start1 += toRead;
|
|
if (start1 >= end)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (discNo == diskCount)
|
|
{
|
|
break;
|
|
}
|
|
start1 = diskOffsets[idx - 4];
|
|
idx++;
|
|
end = start1 + 0xC0000;
|
|
|
|
}
|
|
}
|
|
}
|
|
sha224.TransformFinalBlock(buffer.ToArray(), 0, 0);
|
|
sha224.Hash.CopyTo(dataDigest);
|
|
|
|
stream.Seek(pbpHeader.DataPsarOff, SeekOrigin.Begin);
|
|
readSize = stream.Read(buffer[..0x100]);
|
|
if (readSize == 0x100)
|
|
{
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
ret = unchecked((int)0x80870005);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static int SceNpDrmEbootSigGen(string fileName, int type, Span<byte> ebootSig, int swVer, Span<byte> buffer, int blockSize)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fileName)) return -0x7f78ffff;
|
|
var fi = new FileInfo(fileName);
|
|
if (!fi.Exists)
|
|
{
|
|
return -1;
|
|
}
|
|
using (FileStream fstream = fi.OpenRead())
|
|
{
|
|
return SceNpDrmEbootSigGen(fstream, type, ebootSig, swVer, buffer, blockSize);
|
|
}
|
|
|
|
}
|
|
private static int SceNpDrmEbootSigGen(Stream ebootStream, int type, Span<byte> ebootSig, int swVer, Span<byte> buffer, int blockSize)
|
|
{
|
|
Span<byte> pbpHdrDigest = stackalloc byte[32];
|
|
Span<byte> npUmdImgDigest = stackalloc byte[32];
|
|
Span<byte> ebootSigtmp = stackalloc byte[512];
|
|
ref var sceEbootPbp = ref MemoryMarshal.AsRef<SceEbootPbp>(ebootSigtmp);
|
|
if ((blockSize & 0x3F) != 0)
|
|
{
|
|
blockSize &= unchecked((int)0xFFFFFFC0);
|
|
}
|
|
|
|
if (ebootStream == null || buffer == null || ebootSig == null || blockSize < 0x400 || type > 3)
|
|
{
|
|
return -0x7f78ffff;
|
|
}
|
|
|
|
Span<byte> secureTick = BitConverter.GetBytes(SceRtc.ksceRtcGetCurrentSecureTick());
|
|
|
|
ebootSig.Fill(0);
|
|
sceEbootPbp.SwVer = swVer;
|
|
sceEbootPbp.Aid = Aid;
|
|
sceEbootPbp.SecureTick = MemoryMarshal.AsRef<ulong>(secureTick);
|
|
|
|
long flen = ebootStream.Length;
|
|
int ret = SceEbootPbpDigest(ebootStream, flen, pbpHdrDigest, npUmdImgDigest, buffer, blockSize);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
sceEbootPbp.KeyType = 1;
|
|
sceEbootPbp.PbpSize = flen;
|
|
sceEbootPbp.Type = type;
|
|
var psarSig = Encoding.ASCII.GetString(buffer[..8]);
|
|
if (type == 3)
|
|
{
|
|
if (psarSig != "PSISOIMG" && psarSig != "PSTITLEI")
|
|
{
|
|
return -0x7f78fffb;
|
|
}
|
|
sceEbootPbp.Magic = 0x474953315350504E;
|
|
}
|
|
else
|
|
{
|
|
if (psarSig != "NPUMDIMG")
|
|
{
|
|
return -0x7f78fffb;
|
|
}
|
|
|
|
sceEbootPbp.Magic = 0x474953444D55504E;
|
|
buffer.Slice(0x10, 0x30).CopyTo(sceEbootPbp.ContentId);
|
|
}
|
|
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(pbpHdrDigest, sceEbootPbp.PbpHdrSig, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(npUmdImgDigest, sceEbootPbp.NpUmdImgSig, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
var sha224 = SHA224.Create();
|
|
var ebootsigDigst = sha224.ComputeHash(ebootSigtmp[..0x1C8].ToArray());
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(ebootsigDigst, sceEbootPbp.Sig, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ebootSigtmp.CopyTo(ebootSig);
|
|
ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
private static int SceEbootPbpDigest(Stream stream, long fileSize, Span<byte> pbpHdrDigest,
|
|
Span<byte> npUmdImgDigest, Span<byte> buffer, int blockSize)
|
|
{
|
|
var sha224 = SHA224.Create();
|
|
var ret = unchecked((int)0x80870005);
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
buffer = buffer[..blockSize];
|
|
var readSize = stream.Read(buffer);
|
|
if (readSize < 0x28)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
var pbpHeader = MemoryMarshal.AsRef<PbpHeader>(buffer);
|
|
if (fileSize < pbpHeader.DataPsarOff + 0xFF || pbpHeader.Sig != 0x50425000)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
var tmp = readSize;
|
|
if (pbpHeader.DataPsarOff < readSize)
|
|
{
|
|
tmp = pbpHeader.DataPsarOff;
|
|
}
|
|
|
|
var paramReadSize = pbpHeader.Icon0Off;
|
|
if (paramReadSize > 0x400)
|
|
{
|
|
paramReadSize = 0x400;
|
|
}
|
|
|
|
if (tmp < paramReadSize)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
sha224.TransformFinalBlock(buffer.ToArray(), 0, paramReadSize);
|
|
sha224.Hash.CopyTo(pbpHdrDigest);
|
|
|
|
sha224.Initialize();
|
|
|
|
var alignedFileSize = (pbpHeader.DataPsarOff + 0x1C0000 + 64 - 1) & ~(64 - 1);
|
|
if (alignedFileSize < fileSize)
|
|
{
|
|
fileSize = alignedFileSize;
|
|
}
|
|
|
|
var fixsize2 = fileSize;
|
|
if (pbpHeader.DataPsarOff + 0x1C0000 < fileSize)
|
|
{
|
|
fixsize2 = pbpHeader.DataPsarOff + 0x1C0000;
|
|
}
|
|
|
|
if (pbpHeader.DataPsarOff < fixsize2)
|
|
{
|
|
var offset = pbpHeader.DataPsarOff;
|
|
stream.Seek(offset, SeekOrigin.Begin);
|
|
while (true)
|
|
{
|
|
readSize = stream.Read(buffer);
|
|
var bsize = readSize;
|
|
if (readSize <= 0)
|
|
{
|
|
ret = unchecked((int)0x80870002);
|
|
return ret;
|
|
}
|
|
|
|
if (fixsize2 < offset + readSize)
|
|
{
|
|
bsize = (int)(fixsize2 - offset);
|
|
}
|
|
sha224.TransformBlock(buffer.ToArray(), 0, bsize, null, 0);
|
|
|
|
offset += readSize;
|
|
if (offset >= fixsize2)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
sha224.TransformFinalBlock(buffer.ToArray(), 0, 0);
|
|
sha224.Hash.CopyTo(npUmdImgDigest);
|
|
stream.Seek(pbpHeader.DataPsarOff, SeekOrigin.Begin);
|
|
readSize = stream.Read(buffer[..0x100]);
|
|
if (readSize == 0x100)
|
|
{
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
ret = unchecked((int)0x80870005);
|
|
}
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static int SceNpDrmPspEbootSigGen(string fileName, Span<byte> ebootSig, Span<byte> buffer, int blockSize)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fileName))
|
|
return -0x7f78ffff;
|
|
|
|
var fi = new FileInfo(fileName);
|
|
if (!fi.Exists)
|
|
{
|
|
return -1;
|
|
}
|
|
using (FileStream fs = fi.OpenRead())
|
|
{
|
|
return SceNpDrmPspEbootSigGen(fs, ebootSig, buffer, blockSize);
|
|
}
|
|
}
|
|
|
|
private static int SceNpDrmPspEbootSigGen(Stream ebootStream, Span<byte> ebootSig, Span<byte> buffer, int blockSize)
|
|
{
|
|
Span<byte> pbpHdrDigest = stackalloc byte[32];
|
|
Span<byte> npUmdImgDigest = stackalloc byte[32];
|
|
Span<byte> ebootSigtmp = stackalloc byte[0x100];
|
|
ref var sceEbootPbp = ref MemoryMarshal.AsRef<SceEbootPbp100>(ebootSigtmp);
|
|
if ((blockSize & 0x3F) != 0)
|
|
{
|
|
blockSize &= unchecked((int)0xFFFFFFC0);
|
|
}
|
|
|
|
if (ebootStream == null || buffer == null || ebootSig == null || blockSize < 0x400)
|
|
{
|
|
return -0x7f78ffff;
|
|
}
|
|
|
|
long fileSize = ebootStream.Length;
|
|
int ret = SceEbootPbpDigest(ebootStream, fileSize, pbpHdrDigest, npUmdImgDigest, buffer, blockSize);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (Encoding.ASCII.GetString(buffer[..8]) != "NPUMDIMG")
|
|
{
|
|
return -0x7f78fffb;
|
|
}
|
|
buffer[..0x40].CopyTo(ebootSigtmp);
|
|
sceEbootPbp.Type = 0;
|
|
sceEbootPbp.Magic = 0x474953444D55504E;
|
|
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(pbpHdrDigest, sceEbootPbp.PbpHdrSig, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(npUmdImgDigest, sceEbootPbp.NpUmdImgSig, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
var sha224 = SHA224.Create();
|
|
var ebootsigDigst = sha224.ComputeHash(ebootSigtmp[..0xC8].ToArray());
|
|
ret = SceSblGcAuthMgrDrmBBForDriver_050DC6DF(ebootsigDigst, sceEbootPbp.Sig, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
ebootSigtmp.CopyTo(ebootSig);
|
|
ret = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static int SceSblGcAuthMgrDrmBBForDriver_050DC6DF(ReadOnlySpan<byte> digest, Span<byte> sig, int type)
|
|
{
|
|
var curve = ECDsaHelper.SetCurve(KeyVault.Eboot_p, KeyVault.Eboot_a, KeyVault.Eboot_b, KeyVault.Eboot_N, KeyVault.Eboot_Gx,
|
|
KeyVault.Eboot_Gy);
|
|
using var ecdsa = ECDsaHelper.Create(curve,
|
|
KeyVault.Eboot_priv[type],
|
|
KeyVault.Eboot_pubx[type],
|
|
KeyVault.Eboot_puby[type], true);
|
|
var signature = ecdsa.SignHash(digest.ToArray());
|
|
signature.CopyTo(sig);
|
|
return 0;
|
|
}
|
|
|
|
private static int SceSblGcAuthMgrDrmBBForDriver_4B506BE7(ReadOnlySpan<byte> digest, ReadOnlySpan<byte> sig, int keyType)
|
|
{
|
|
byte[] pubx;
|
|
byte[] puby;
|
|
switch (keyType)
|
|
{
|
|
case 1:
|
|
pubx = KeyVault.VitaKirk18PubKey1x;
|
|
puby = KeyVault.VitaKirk18PubKey1y;
|
|
break;
|
|
case 0:
|
|
pubx = KeyVault.VitaKirk18PubKey0x;
|
|
puby = KeyVault.VitaKirk18PubKey0y;
|
|
break;
|
|
case 1000:
|
|
pubx = KeyVault.VitaKirk18PubKey1000x;
|
|
puby = KeyVault.VitaKirk18PubKey1000y;
|
|
break;
|
|
default:
|
|
return unchecked((int)0x808a040a);
|
|
}
|
|
var curve = ECDsaHelper.SetCurve(KeyVault.Eboot_p, KeyVault.Eboot_a, KeyVault.Eboot_b, KeyVault.Eboot_N, KeyVault.Eboot_Gx,
|
|
KeyVault.Eboot_Gy);
|
|
using var ecdsa = ECDsaHelper.Create(curve, pubx, puby);
|
|
var verify = ecdsa.VerifyHash(digest.ToArray(), sig.ToArray());
|
|
|
|
return verify ? 0 : -1;
|
|
}
|
|
}
|
|
}
|