Move to .NET Core, and use PspCrypto and LiLib, etc from chovy-sign2

This commit is contained in:
Li 2023-07-10 15:58:32 +12:00
parent 47c67e61cf
commit 20115d25cb
78 changed files with 13644 additions and 39057 deletions

8
.gitignore vendored
View File

@ -2,8 +2,16 @@
*/obj/*
*/bin/*
Thumbs.Db
CHOVY-GEN/Debug/*
CHOVY-GEN/Release/*
LiLib/bin/*
LiLib/obj/*
Vita/bin/*
Vita/lib/*
CHOVY-TRANSFER/obj/*
CHOVY-TRANSFER/bin/*

View File

@ -1,163 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="chovy-gen.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="key_vault.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{DCDBF747-DFB6-450E-A403-1C592D20EAEB}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>CHOVYGEN</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)CHOVY-TRANSFER\bin\$(Configuration)</OutDir>
<TargetName>CHOVY</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IgnoreImportLibrary />
<TargetName>CHOVY</TargetName>
<OutDir>$(SolutionDir)CHOVY-TRANSFER\bin\$(Configuration)</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;CHOVYGEN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\openssl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>C:\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>ws2_32.lib;libsslMT.lib;Crypt32.lib;libcryptoMT.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;CHOVYGEN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;CHOVYGEN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>C:\openssl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>C:\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>ws2_32.lib;libsslMT.lib;Crypt32.lib;libcryptoMT.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;CHOVYGEN_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="key_vault.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="chovy-gen.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerCommand>$(SolutionDir)CHOVY-TRANSFER\bin\$(Configuration)\CHOVY-TRANSFER.EXE</LocalDebuggerCommand>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerDebuggerType>Mixed</LocalDebuggerDebuggerType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerCommand>$(SolutionDir)CHOVY-TRANSFER\bin\$(Configuration)\CHOVY-TRANSFER.EXE</LocalDebuggerCommand>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerDebuggerType>Mixed</LocalDebuggerDebuggerType>
</PropertyGroup>
</Project>

View File

@ -1,289 +0,0 @@
//Project Chovy - __sce_ebootpbp generator by @dots_tb and motoharu
// With CBPS help especially: @SiliCart, @nyaaasen, @notzecoxao (and his friends?)
//Check out motoharu's project: https://github.com/motoharu-gosuto/psvcmd56/blob/master/src/CMD56Reversed/F00D/GcAuthMgrService.cpp#L1102
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <openssl/obj_mac.h>
#include <openssl/ec.h>
#include "key_vault.h"
PACK(typedef struct
{
uint8_t r[0x1c];
uint8_t s[0x1c];
} ECDSA_SIG_0x1c);
PACK(typedef struct sce_ebootpbp {
uint64_t magic;
uint32_t key_type;// set to 1 (maybe keytype?)
uint32_t type;// 03 - ps1, 02 - psp
uint8_t np_title[0x30];
uint64_t aid;
uint64_t secure_tick;
uint64_t filesz;
uint64_t sw_ver;
uint8_t padding[0xf8];
ECDSA_SIG_0x1c ebootpbp_hdr_sig;
ECDSA_SIG_0x1c NPUMDIMG_sig;
ECDSA_SIG_0x1c sceebootpbp_sig;
} sce_ebootpbp);
typedef struct pbp_hdr {
uint32_t magic;
uint32_t unk;
uint32_t sfo_offset;
uint32_t icon0_offset;
uint32_t icon1_offset;
uint32_t pic0_offset;
uint32_t pic1_offset;
uint32_t snd0_offset;
uint32_t data_psp_offset;
uint32_t data_psar_offset;
} pbp_hdr;
#define PSAR_SZ 0x1C0000
#define WORK_BUF_SZ 0x7c0
//based motoharu
__declspec(dllexport) int can_be_reversed_80C17A(const uint8_t* src, int some_size, uint8_t* iv, uint8_t* src_xored_digest)
{
unsigned char src_xored[0x20];
memcpy(src_xored, iv, 0x20);
if (some_size > 0x20)
return 0x12;
for(int i = 0; i < some_size; i++)
src_xored[i] = src[i] ^ iv[i];
int r0;
SHA256_CTX sha256_ctx;
SHA256_Init(&sha256_ctx);
SHA256_Update(&sha256_ctx, src_xored, 0x20);
r0 = SHA256_Final(src_xored_digest, &sha256_ctx);
if(r0 != 1)
return 0x11;
for(int i = 0; i < 0x20; i++)
iv[i] = src_xored_digest[i] ^ iv[i];
for(int i = 0; i < 0x20; i++)
{
if(iv[i] != 0xFF)
{
iv[i] = iv[i] + 1;
break;
}
iv[i] = 0;
}
return 0;
}
__declspec(dllexport) int f00d_KIRK0x22(const uint8_t *hash, ECDSA_SIG_0x1c *signature, int key_sel) {
uint8_t hmac_in[0x38];
uint8_t hmac_hash_iv[0x38];
memcpy(&hmac_in, hash, 0x1c);
memcpy(&hmac_in[0x1c], &keyvault_ec_privkey[key_sel], 0x1c);
HMAC_CTX *hmac_ctx = HMAC_CTX_new();
HMAC_Init_ex(hmac_ctx, &hmac_key_0x22, 0x40, EVP_sha256(), NULL);
HMAC_Update(hmac_ctx, &hmac_in, 0x1c << 1);
unsigned int len;
HMAC_Final(hmac_ctx, &hmac_hash_iv, &len);
HMAC_CTX_free(hmac_ctx);
uint8_t sha256_out[0x40];
int ret = 0;
do {
ret = can_be_reversed_80C17A(hash, 0x1c, hmac_hash_iv, &sha256_out[0]);
if(ret != 0 || (ret = can_be_reversed_80C17A(hash, 0x1c, hmac_hash_iv, &sha256_out[0x20])) != 0)
return 0;
} while(ret != 0);
//ECDSA
BIGNUM *a = BN_bin2bn(keyvault_ec_a, 0x1c, NULL),
*b = BN_bin2bn(keyvault_ec_b, 0x1c, NULL),
*p = BN_bin2bn(keyvault_ec_p, 0x1c, NULL),
*order = BN_bin2bn(keyvault_ec_N, 0x1c, NULL),
*x = BN_bin2bn(keyvault_ec_Gx, 0x1c, NULL),
*y = BN_bin2bn(keyvault_ec_Gy, 0x1c, NULL),
*priv_key = BN_bin2bn(keyvault_ec_privkey[key_sel], 0x1c, NULL),
*m = BN_bin2bn(sha256_out, 0x3c, NULL);
BN_CTX *bn_ctx = BN_CTX_new();
BN_MONT_CTX *bn_mon_ctx = BN_MONT_CTX_new();
BN_MONT_CTX_set(bn_mon_ctx, order, bn_ctx);
EC_GROUP *curve = EC_GROUP_new_curve_GFp(p, a, b, bn_ctx);
EC_POINT *generator = EC_POINT_new(curve);
EC_POINT_set_affine_coordinates_GFp(curve, generator, x, y, bn_ctx);
EC_GROUP_set_generator(curve, generator, order, NULL);
EC_KEY *eckey=EC_KEY_new();
EC_KEY_set_group(eckey,curve);
EC_KEY_set_private_key(eckey, priv_key);
m = BN_bin2bn(sha256_out, 0x3c, NULL);
BN_mod(m, m, order, bn_ctx);
//Generate R in order to get custom "random number"
BIGNUM *sig_r = BN_new();
EC_POINT_mul(curve, generator, m, NULL, NULL, bn_ctx);
EC_POINT_get_affine_coordinates_GFp(curve, generator, sig_r, NULL, bn_ctx);
BN_nnmod(sig_r, sig_r, order, bn_ctx);
//Generate M^-1
BIGNUM *exp = BN_new();
BIGNUM *minv = BN_new();
BN_set_word(exp, (BN_ULONG)2);
BN_sub(exp, order, exp);
BN_mod_exp_mont(minv, m, exp, order, bn_ctx, bn_mon_ctx);
ECDSA_SIG *sig = ECDSA_do_sign_ex(hash, 0x1c, minv, sig_r, eckey);
if(!sig) {
ret = 0;
goto error;
}
BIGNUM *sig_s;
ECDSA_SIG_get0(sig, NULL, &sig_s);
BN_bn2bin(sig_r, &signature->r);
BN_bn2bin(sig_s, &signature->s);
ECDSA_SIG_free(sig);
//BN_free(sig_s);
ret = 1;
error:
BN_free(sig_r);
EC_POINT_free(generator);
BN_free(y);
BN_free(x);
BN_free(order);
BN_free(p);
BN_free(b);
BN_free(a);
BN_free(minv);
BN_free(exp);
BN_free(priv_key);
BN_CTX_free(bn_ctx);
BN_MONT_CTX_free(bn_mon_ctx);
return ret;
}
__declspec(dllexport) int chovy_gen(char *ebootpbp, uint64_t signaid, char *outscefile)
{
int ret = 1;
FILE *fin = 0, *fout = 0;
uint8_t *work_buf = (unsigned char*) calloc (1, WORK_BUF_SZ);
sce_ebootpbp *sceebootpbp_file = (unsigned char*) calloc (1, sizeof(sce_ebootpbp));
fin = fopen(ebootpbp, "rb");
if (!fin) {
goto error;
}
memcpy(&sceebootpbp_file->magic, "NPUMDSIG", 0x8);
sceebootpbp_file->type = 2;
sceebootpbp_file->key_type = 1;
sceebootpbp_file->aid = signaid;
fseek(fin, 0, SEEK_END);
sceebootpbp_file->filesz = ftell(fin);
pbp_hdr hdr;
fseek(fin, 0, SEEK_SET);
fread(&hdr, sizeof(pbp_hdr),1,fin);
fseek(fin, 0, SEEK_SET);
fread(work_buf, hdr.icon0_offset, 1,fin);
uint8_t work_hash[0x1c];
SHA256_CTX sha256_ctx;
SHA224_Init(&sha256_ctx);
SHA224_Update(&sha256_ctx, work_buf, hdr.icon0_offset);
SHA224_Final(work_hash, &sha256_ctx);
f00d_KIRK0x22(work_hash, &sceebootpbp_file->ebootpbp_hdr_sig, sceebootpbp_file->key_type);
SHA224_Init(&sha256_ctx);
fseek(fin, hdr.data_psar_offset, SEEK_SET);
size_t size = PSAR_SZ;
int to_read = size > WORK_BUF_SZ ? WORK_BUF_SZ : size;
fread(work_buf, to_read, 1,fin);
if(memcmp(work_buf, "NPUMDIMG", 0x8) == 0)
memcpy(&sceebootpbp_file->np_title, work_buf + 0x10, sizeof(sceebootpbp_file->np_title));
else {
memcpy(&sceebootpbp_file->magic, "NPPS1SIG", sizeof(sceebootpbp_file->magic));
sceebootpbp_file->type = 3;
}
do {
size -= to_read;
SHA224_Update(&sha256_ctx, work_buf, to_read);
to_read = size > WORK_BUF_SZ ? WORK_BUF_SZ : size;
fread(work_buf, to_read, 1,fin);
} while(size > 0);
SHA224_Final(work_hash, &sha256_ctx);
f00d_KIRK0x22(work_hash, &sceebootpbp_file->NPUMDIMG_sig, sceebootpbp_file->key_type);
SHA224_Init(&sha256_ctx);
SHA224_Update(&sha256_ctx, sceebootpbp_file, 0x1C8);
SHA224_Final(work_hash, &sha256_ctx);
f00d_KIRK0x22(work_hash, &sceebootpbp_file->sceebootpbp_sig, sceebootpbp_file->key_type);
fout = fopen(outscefile, "wb");
if (!fout) {
goto error;
}
fwrite(sceebootpbp_file, 1, sizeof(sce_ebootpbp), fout);
ret = 0;
error:
if (fin)
fclose(fin);
if (fout)
fclose(fout);
free(work_buf);
free(sceebootpbp_file);
return ret;
}

View File

@ -1,17 +0,0 @@
uint8_t keyvault_ec_p[0x1c] = {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};
uint8_t keyvault_ec_a[0x1c] = {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};
uint8_t keyvault_ec_b[0x1c] = {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};
uint8_t keyvault_ec_N[0x1c] = { 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};
uint8_t keyvault_ec_Gx[0x1c] = {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};
uint8_t keyvault_ec_Gy[0x1c] = {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};
uint8_t keyvault_ec_pubkey[2][0x38] = {
{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, 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},
{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, 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}
};
uint8_t keyvault_ec_privkey[2][0x1c] = {
{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},
{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}
};
uint8_t hmac_key_0x22[0x40] = {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};

View File

@ -1,11 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.76
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CHOVY-TRANSFER", "CHOVY-TRANSFER\CHOVY-TRANSFER.csproj", "{B4CAD2C0-BA54-46B6-A8D0-43A9C2390D3C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CHOVY-TRANSFER", "CHOVY-TRANSFER\CHOVY-TRANSFER.csproj", "{B4CAD2C0-BA54-46B6-A8D0-43A9C2390D3C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CHOVY-GEN", "CHOVY-GEN\CHOVY-GEN.vcxproj", "{DCDBF747-DFB6-450E-A403-1C592D20EAEB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PspCrypto", "PspCrypto\PspCrypto.csproj", "{C93D6CE7-989F-4C89-A29B-9684C9297184}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vita", "Vita\Vita.csproj", "{8463BD1A-EC79-4469-A383-26020298F512}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -29,20 +30,35 @@ Global
{B4CAD2C0-BA54-46B6-A8D0-43A9C2390D3C}.Release|x64.Build.0 = Release|Any CPU
{B4CAD2C0-BA54-46B6-A8D0-43A9C2390D3C}.Release|x86.ActiveCfg = Release|Any CPU
{B4CAD2C0-BA54-46B6-A8D0-43A9C2390D3C}.Release|x86.Build.0 = Release|Any CPU
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Debug|Any CPU.ActiveCfg = Debug|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Debug|Any CPU.Build.0 = Debug|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Debug|x64.ActiveCfg = Debug|x64
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Debug|x64.Build.0 = Debug|x64
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Debug|x86.ActiveCfg = Debug|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Debug|x86.Build.0 = Debug|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Release|Any CPU.ActiveCfg = Release|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Release|Any CPU.Build.0 = Release|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Release|x64.ActiveCfg = Release|x64
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Release|x64.Build.0 = Release|x64
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Release|x86.ActiveCfg = Release|Win32
{DCDBF747-DFB6-450E-A403-1C592D20EAEB}.Release|x86.Build.0 = Release|Win32
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Debug|x64.ActiveCfg = Debug|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Debug|x64.Build.0 = Debug|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Debug|x86.ActiveCfg = Debug|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Debug|x86.Build.0 = Debug|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Release|Any CPU.Build.0 = Release|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Release|x64.ActiveCfg = Release|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Release|x64.Build.0 = Release|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Release|x86.ActiveCfg = Release|Any CPU
{C93D6CE7-989F-4C89-A29B-9684C9297184}.Release|x86.Build.0 = Release|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Debug|x64.ActiveCfg = Debug|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Debug|x64.Build.0 = Debug|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Debug|x86.ActiveCfg = Debug|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Debug|x86.Build.0 = Debug|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Release|Any CPU.Build.0 = Release|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Release|x64.ActiveCfg = Release|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Release|x64.Build.0 = Release|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Release|x86.ActiveCfg = Release|Any CPU
{8463BD1A-EC79-4469-A383-26020298F512}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D2939D13-1FC7-4C9B-95D9-BA3B53E08138}
EndGlobalSection
EndGlobal

View File

@ -2,5 +2,5 @@
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup>
</configuration>

View File

@ -1,107 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B4CAD2C0-BA54-46B6-A8D0-43A9C2390D3C}</ProjectGuid>
<TargetFramework>net6.0-windows</TargetFramework>
<OutputType>WinExe</OutputType>
<RootNamespace>CHOVY_TRANSFER</RootNamespace>
<AssemblyName>CHOVY-TRANS</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>true</Prefer32Bit>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>true</Prefer32Bit>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>chovy-trans.ico</ApplicationIcon>
<AssemblyTitle>CHOVY-TRANSFER</AssemblyTitle>
<Product>CHOVY-TRANSFER</Product>
<Copyright>Copyright © 2019</Copyright>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.8.5.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="DotNetZip, Version=1.13.4.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.13.4\lib\net40\DotNetZip.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CHOVYTRANSFER.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="CHOVYTRANSFER.Designer.cs">
<DependentUpon>CHOVYTRANSFER.cs</DependentUpon>
</Compile>
<Compile Include="cmakeys.cs" />
<Compile Include="param.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PSVIMGBuilder.cs" />
<Compile Include="PSVIMGStructs.cs" />
<Compile Include="PSVMDBuilder.cs" />
<EmbeddedResource Include="CHOVYTRANSFER.resx">
<DependentUpon>CHOVYTRANSFER.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="chovy-trans.ico" />
<None Include="chovytrans.gif" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\PspCrypto\PspCrypto.csproj" />
<ProjectReference Include="..\Vita\Vita.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>C:\Users\Li\Documents\git\chovy-trans\CHOVY-TRANSFER\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
<ItemGroup>
<Compile Update="CHOVYTRANSFER.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@ -84,9 +84,10 @@
"X:\\",
"Y:\\",
"Z:\\"});
this.driveLetterSrc.Location = new System.Drawing.Point(74, 12);
this.driveLetterSrc.Location = new System.Drawing.Point(86, 14);
this.driveLetterSrc.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.driveLetterSrc.Name = "driveLetterSrc";
this.driveLetterSrc.Size = new System.Drawing.Size(41, 21);
this.driveLetterSrc.Size = new System.Drawing.Size(47, 23);
this.driveLetterSrc.TabIndex = 1;
this.driveLetterSrc.SelectedIndexChanged += new System.EventHandler(this.driveLetterSrc_SelectedIndexChanged);
//
@ -95,15 +96,18 @@
this.pspFolder.BackColor = System.Drawing.Color.DimGray;
this.pspFolder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pspFolder.ForeColor = System.Drawing.Color.Lime;
this.pspFolder.Location = new System.Drawing.Point(121, 13);
this.pspFolder.Location = new System.Drawing.Point(141, 15);
this.pspFolder.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.pspFolder.Name = "pspFolder";
this.pspFolder.Size = new System.Drawing.Size(194, 20);
this.pspFolder.Size = new System.Drawing.Size(226, 23);
this.pspFolder.TabIndex = 2;
this.pspFolder.Text = "PSP";
this.pspFolder.TextChanged += new System.EventHandler(this.pspFolder_TextChanged);
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.BackColor = System.Drawing.Color.Black;
this.groupBox1.Controls.Add(this.cmaDir);
this.groupBox1.Controls.Add(this.driveLetterDst);
@ -112,21 +116,26 @@
this.groupBox1.Controls.Add(this.driveLetterSrc);
this.groupBox1.Controls.Add(this.pspFolder);
this.groupBox1.ForeColor = System.Drawing.Color.Lime;
this.groupBox1.Location = new System.Drawing.Point(162, 12);
this.groupBox1.Location = new System.Drawing.Point(189, 14);
this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(622, 41);
this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.groupBox1.Size = new System.Drawing.Size(726, 47);
this.groupBox1.TabIndex = 3;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Directories";
//
// cmaDir
//
this.cmaDir.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.cmaDir.BackColor = System.Drawing.Color.DimGray;
this.cmaDir.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.cmaDir.ForeColor = System.Drawing.Color.Lime;
this.cmaDir.Location = new System.Drawing.Point(439, 14);
this.cmaDir.Location = new System.Drawing.Point(512, 14);
this.cmaDir.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.cmaDir.Name = "cmaDir";
this.cmaDir.Size = new System.Drawing.Size(177, 20);
this.cmaDir.Size = new System.Drawing.Size(206, 23);
this.cmaDir.TabIndex = 6;
this.cmaDir.Text = "Users\\XXX\\Documents\\PS Vita";
this.cmaDir.TextChanged += new System.EventHandler(this.cmaDir_TextChanged);
@ -166,59 +175,70 @@
"X:\\",
"Y:\\",
"Z:\\"});
this.driveLetterDst.Location = new System.Drawing.Point(392, 12);
this.driveLetterDst.Location = new System.Drawing.Point(457, 13);
this.driveLetterDst.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.driveLetterDst.Name = "driveLetterDst";
this.driveLetterDst.Size = new System.Drawing.Size(41, 21);
this.driveLetterDst.Size = new System.Drawing.Size(47, 23);
this.driveLetterDst.TabIndex = 5;
this.driveLetterDst.SelectedIndexChanged += new System.EventHandler(this.driveLetterDst_SelectedIndexChanged);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(321, 16);
this.label2.Location = new System.Drawing.Point(376, 17);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(65, 13);
this.label2.Size = new System.Drawing.Size(73, 15);
this.label2.TabIndex = 4;
this.label2.Text = "CMA Folder:";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(11, 16);
this.label1.Location = new System.Drawing.Point(13, 18);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(63, 13);
this.label1.Size = new System.Drawing.Size(66, 15);
this.label1.TabIndex = 3;
this.label1.Text = "PSP Folder:";
//
// pspGames
//
this.pspGames.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pspGames.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.pspGames.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pspGames.ForeColor = System.Drawing.Color.Lime;
this.pspGames.FormattingEnabled = true;
this.pspGames.Location = new System.Drawing.Point(162, 73);
this.pspGames.ItemHeight = 15;
this.pspGames.Location = new System.Drawing.Point(189, 84);
this.pspGames.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.pspGames.Name = "pspGames";
this.pspGames.Size = new System.Drawing.Size(622, 223);
this.pspGames.Size = new System.Drawing.Size(725, 257);
this.pspGames.TabIndex = 4;
//
// label3
//
this.label3.AutoSize = true;
this.label3.ForeColor = System.Drawing.Color.Lime;
this.label3.Location = new System.Drawing.Point(159, 56);
this.label3.Location = new System.Drawing.Point(186, 65);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(82, 13);
this.label3.Size = new System.Drawing.Size(86, 15);
this.label3.TabIndex = 5;
this.label3.Text = "Games on PSP:";
//
// transVita
//
this.transVita.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.transVita.BackColor = System.Drawing.Color.Black;
this.transVita.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.transVita.ForeColor = System.Drawing.Color.Red;
this.transVita.Location = new System.Drawing.Point(649, 302);
this.transVita.Location = new System.Drawing.Point(757, 348);
this.transVita.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.transVita.Name = "transVita";
this.transVita.Size = new System.Drawing.Size(135, 23);
this.transVita.Size = new System.Drawing.Size(158, 27);
this.transVita.TabIndex = 6;
this.transVita.Text = "MOV 1, PSVITA";
this.transVita.UseVisualStyleBackColor = false;
@ -227,56 +247,68 @@
//
// progressBar
//
this.progressBar.Location = new System.Drawing.Point(202, 302);
this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBar.Location = new System.Drawing.Point(236, 348);
this.progressBar.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(441, 23);
this.progressBar.Size = new System.Drawing.Size(514, 27);
this.progressBar.TabIndex = 7;
//
// progressStatus
//
this.progressStatus.AutoSize = true;
this.progressStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.progressStatus.ForeColor = System.Drawing.Color.Lime;
this.progressStatus.Location = new System.Drawing.Point(159, 307);
this.progressStatus.Location = new System.Drawing.Point(186, 354);
this.progressStatus.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.progressStatus.Name = "progressStatus";
this.progressStatus.Size = new System.Drawing.Size(21, 13);
this.progressStatus.Size = new System.Drawing.Size(42, 17);
this.progressStatus.TabIndex = 8;
this.progressStatus.Text = "0%";
this.progressStatus.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// pictureBox1
//
this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.pictureBox1.BackgroundImage = global::CHOVY_TRANSFER.Properties.Resources.chovytrans;
this.pictureBox1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.pictureBox1.Location = new System.Drawing.Point(9, 8);
this.pictureBox1.Location = new System.Drawing.Point(10, 9);
this.pictureBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(144, 330);
this.pictureBox1.Size = new System.Drawing.Size(168, 381);
this.pictureBox1.TabIndex = 9;
this.pictureBox1.TabStop = false;
//
// dexToggle
//
this.dexToggle.Location = new System.Drawing.Point(781, 0);
this.dexToggle.Location = new System.Drawing.Point(911, 0);
this.dexToggle.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.dexToggle.Name = "dexToggle";
this.dexToggle.Size = new System.Drawing.Size(16, 16);
this.dexToggle.Size = new System.Drawing.Size(19, 18);
this.dexToggle.TabIndex = 10;
this.dexToggle.TabStop = false;
this.dexToggle.Click += new System.EventHandler(this.dexToggle_Click);
//
// currentFile
//
this.currentFile.AutoSize = true;
this.currentFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.currentFile.ForeColor = System.Drawing.Color.Lime;
this.currentFile.Location = new System.Drawing.Point(173, 328);
this.currentFile.Location = new System.Drawing.Point(186, 378);
this.currentFile.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.currentFile.Name = "currentFile";
this.currentFile.Size = new System.Drawing.Size(55, 13);
this.currentFile.Size = new System.Drawing.Size(728, 17);
this.currentFile.TabIndex = 11;
this.currentFile.Text = "Waiting ...";
this.currentFile.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// CHOVYTRANSFER
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Black;
this.ClientSize = new System.Drawing.Size(796, 350);
this.ClientSize = new System.Drawing.Size(929, 404);
this.Controls.Add(this.currentFile);
this.Controls.Add(this.dexToggle);
this.Controls.Add(this.pictureBox1);
@ -287,6 +319,7 @@
this.Controls.Add(this.pspGames);
this.Controls.Add(this.groupBox1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.Name = "CHOVYTRANSFER";
this.Text = "Chovy-Transfer";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CHOVYTRANSFER_FormClosing);

View File

@ -2,22 +2,42 @@
using System;
using System.IO;
using System.Windows.Forms;
using Param_SFO;
using System.Runtime.InteropServices;
using System.Text;
using KeyDerivation;
using PSVIMGTOOLS;
using System.Drawing;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using Li.Progress;
using Param;
using PspCrypto;
using Vita.ContentManager;
using Vita.PsvImgTools;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace CHOVY_TRANSFER
{
public partial class CHOVYTRANSFER : Form
{
[DllImport("CHOVY.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int chovy_gen(string ebootpbp, UInt64 AID, string outscefile);
const int FW_VERSION = 0x3600000;
private byte[] chovy_gen(string ebootpbp, UInt64 AID)
{
bool ps1 = IsPs1(ebootpbp);
using (FileStream fs = File.OpenRead(ebootpbp))
{
byte[] ebootSig = new byte[0x200];
SceNpDrm.Aid = AID;
if(ps1)
SceNpDrm.KsceNpDrmEbootSigGenPs1(fs, ebootSig, FW_VERSION);
else
SceNpDrm.KsceNpDrmEbootSigGenPsp(fs, ebootSig, FW_VERSION);
return ebootSig;
}
}
public bool IsDexAidSet()
{
@ -31,55 +51,6 @@ namespace CHOVY_TRANSFER
return true;
}
}
public string GetCmaDir()
{
string Dir = "";
try
{
//try qcma
Dir = Registry.CurrentUser.OpenSubKey(@"Software\codestation\qcma").GetValue("appsPath").ToString();
}
catch (Exception)
{
try
{
//try sony cma
Dir = Registry.CurrentUser.OpenSubKey(@"Software\Sony Corporation\Content Manager Assistant\Settings").GetValue("ApplicationHomePath").ToString();
}
catch (Exception)
{
try
{
//try devkit cma
Dir = Registry.CurrentUser.OpenSubKey(@"Software\SCE\PSP2\Services\Content Manager Assistant for PlayStation(R)Vita DevKit\Settings").GetValue("ApplicationHomePath").ToString();
}
catch (Exception)
{
try
{
string DefaultDir = Path.Combine(Environment.GetEnvironmentVariable("HOMEDRIVE"), Environment.GetEnvironmentVariable("HOMEPATH"), "Documents", "PS Vita");
if (Directory.Exists(DefaultDir))
{
Dir = DefaultDir;
}
}
catch (Exception)
{
//Do nothing
}
}
}
}
if (ReadSetting("CmaDir") != "")
{
Dir = ReadSetting("CmaDir");
}
return Dir.Replace("/","\\");
}
public string ReadSetting(string Setting)
{
@ -160,13 +131,10 @@ namespace CHOVY_TRANSFER
string Title = GetTitleFromPbp(EbootPbp);
string ContentId = GetContentIdFromPbp(EbootPbp);
string LicenseFile = Path.Combine(PspDir, "LICENSE", ContentId + ".RIF");
string LicenseFile = Path.Combine(PspDir, "LICENSE", ContentId);
if (TitleId.Length == 9 && File.Exists(LicenseFile));
{
if (TitleId.Length == 9 && File.Exists(LicenseFile))
pspGames.Items.Add(TitleId + " - " + Title);
}
}
catch (Exception) { };
@ -194,12 +162,8 @@ namespace CHOVY_TRANSFER
public string GetTitleFromPbp(string pbp)
{
byte[] SfoData = GetSfo(pbp);
using (MemoryStream ms = new MemoryStream(SfoData, 0x00, SfoData.Length))
{
PARAM_SFO sfo = new PARAM_SFO(ms);
return sfo.Title;
}
Sfo sfo = Sfo.ReadSfo(SfoData);
return sfo["TITLE"] as String;
}
public byte[] GetSfo(string pbp)
@ -338,8 +302,14 @@ namespace CHOVY_TRANSFER
pspFolder.Text = PspDir;
}
string cmaDir = ReadSetting("CmaDir");
if(cmaDir == "")
cmaDir = SettingsReader.BackupsFolder;
SettingsReader.BackupsFolder = cmaDir;
ChangePspDir(FindPspDir());
ChangeCmaDir(GetCmaDir());
ChangeCmaDir(SettingsReader.BackupsFolder);
PopulatePspGameList();
}
@ -356,11 +326,13 @@ namespace CHOVY_TRANSFER
private void cmaDir_TextChanged(object sender, EventArgs e)
{
WriteSetting("CmaDir", Path.Combine(driveLetterDst.Text, cmaDir.Text));
string dir = Path.Combine(driveLetterDst.Text, cmaDir.Text);
WriteSetting("CmaDir", dir);
SettingsReader.BackupsFolder = dir;
}
private void driveLetterDst_SelectedIndexChanged(object sender, EventArgs e)
{
WriteSetting("CmaDir", Path.Combine(driveLetterDst.Text, cmaDir.Text));
cmaDir_TextChanged(sender, e);
}
private void transVita_EnabledChanged(object sender, EventArgs e)
{
@ -376,10 +348,10 @@ namespace CHOVY_TRANSFER
Environment.Exit(0);
}
private void transVita_Click(object sender, EventArgs e)
private async void transVita_Click(object sender, EventArgs e)
{
if(!Directory.Exists(Path.Combine(driveLetterDst.Text, cmaDir.Text)))
if(!Directory.Exists(SettingsReader.BackupsFolder))
{
MessageBox.Show("CMA Folder Doesn't Exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
@ -404,19 +376,23 @@ namespace CHOVY_TRANSFER
cmaDir.ReadOnly = true;
pspGames.Enabled = false;
bool isDlc = false;
string titleId = pspGames.SelectedItem.ToString().Substring(0, 9);
string gameFolder = Path.Combine(driveLetterSrc.Text, pspFolder.Text, "GAME", titleId);
string pspDir = Path.Combine(driveLetterSrc.Text, pspFolder.Text);
string gameFolder = Path.Combine(pspDir, "GAME", titleId);
string ebootFile = Path.Combine(gameFolder, "EBOOT.PBP");
if (!File.Exists(ebootFile))
{
isDlc = true;
ebootFile = Path.Combine(gameFolder, "PARAM.PBP");
}
List<string> licenseFiles = new List<string>();
string cid = GetContentIdFromPbp(ebootFile);
licenseFiles.Add(Path.Combine(driveLetterSrc.Text, pspFolder.Text, "LICENSE", cid + ".RIF"));
licenseFiles.Add(Path.Combine(pspDir, "LICENSE", cid + ".RIF"));
string sigFile = Path.Combine(gameFolder, "__sce_ebootpbp");
string backupDir = Path.Combine(driveLetterDst.Text, cmaDir.Text);
bool isDlc = Path.GetFileName(gameFolder) == "PARAM.PBP";
bool isPs1 = IsPs1(ebootFile);
if (!File.Exists(licenseFiles.First()))
@ -444,74 +420,44 @@ namespace CHOVY_TRANSFER
File.Delete(sigFile);
}
int ChovyGenRes = 100;
Thread ChovyGen = new Thread(() =>
{
ChovyGenRes = chovy_gen(ebootFile, uAid, sigFile);
});
ChovyGen.Start();
while (ChovyGen.IsAlive)
{
Application.DoEvents();
}
if (!File.Exists(sigFile) || ChovyGenRes != 0 && !isDlc)
{
MessageBox.Show("CHOVY-GEN Failed! Please check CHOVY.DLL exists\nand that the Microsoft Visual C++ 2015 Redistributable Update 3 RC is installed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
transVita.Enabled = true;
driveLetterDst.Enabled = true;
driveLetterSrc.Enabled = true;
pspFolder.ReadOnly = false;
cmaDir.ReadOnly = false;
pspGames.Enabled = true;
return;
}
byte[] EbootSig = chovy_gen(ebootFile, uAid);
Account CmaAccount = new Account(uAid);
CmaAccount.Devkit = IsDexAidSet();
/*
* BUILD PSVIMG FILE(s)
*/
// Pacakge GAME
byte[] CmaKey;
if(!IsDexAidSet())
{
CmaKey = CmaKeys.GenerateKey(bAid);
}
else
{
CmaKey = CmaKeys.GenerateKey(new byte[0x8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
}
byte[] CmaKey = CmaAccount.CmaKey;
string[] entrys = Directory.GetFileSystemEntries(gameFolder, "*", SearchOption.AllDirectories);
long noEntrys = entrys.LongLength;
string parentPath = "ux0:pspemu/temp/game/PSP/GAME/" + titleId;
int noBlocks = 0;
foreach (string fileName in Directory.GetFiles(gameFolder, "*", SearchOption.AllDirectories))
{
noBlocks += Convert.ToInt32(new FileInfo(fileName).Length / PSVIMGConstants.PSVIMG_BLOCK_SIZE);
}
progressBar.Maximum = noBlocks;
string pgameFolder;
string pgameFolderl;
string pgameFolderLicense;
string scesys;
if (!isPs1)
{
if(!IsDexAidSet())
{
pgameFolder = Path.Combine(backupDir, "PGAME", sAid, titleId, "game");
pgameFolderl = Path.Combine(backupDir, "PGAME", sAid, titleId, "license");
scesys = Path.Combine(backupDir, "PGAME", sAid, titleId, "sce_sys");
pgameFolder = Path.Combine(SettingsReader.PspFolder, sAid, titleId, "game");
pgameFolderLicense = Path.Combine(SettingsReader.PspFolder, sAid, titleId, "license");
scesys = Path.Combine(SettingsReader.PspFolder, sAid, titleId, "sce_sys");
}
else
{
pgameFolder = Path.Combine(backupDir, "PGAME", "0000000000000000", titleId, "game");
pgameFolderl = Path.Combine(backupDir, "PGAME", "0000000000000000", titleId, "license");
scesys = Path.Combine(backupDir, "PGAME", "0000000000000000", titleId, "sce_sys");
pgameFolder = Path.Combine(SettingsReader.PspFolder, "0000000000000000", titleId, "game");
pgameFolderLicense = Path.Combine(SettingsReader.PspFolder, "0000000000000000", titleId, "license");
scesys = Path.Combine(SettingsReader.PspFolder, "0000000000000000", titleId, "sce_sys");
}
}
@ -519,94 +465,95 @@ namespace CHOVY_TRANSFER
{
if(!IsDexAidSet())
{
pgameFolder = Path.Combine(backupDir, "PSGAME", sAid, titleId, "game");
pgameFolderl = Path.Combine(backupDir, "PSGAME", sAid, titleId, "license");
scesys = Path.Combine(backupDir, "PSGAME", sAid, titleId, "sce_sys");
pgameFolder = Path.Combine(SettingsReader.Ps1Folder, sAid, titleId, "game");
pgameFolderLicense = Path.Combine(SettingsReader.Ps1Folder, sAid, titleId, "license");
scesys = Path.Combine(SettingsReader.Ps1Folder, sAid, titleId, "sce_sys");
}
else
{
pgameFolder = Path.Combine(backupDir, "PSGAME", "0000000000000000", titleId, "game");
pgameFolderl = Path.Combine(backupDir, "PSGAME", "0000000000000000", titleId, "license");
scesys = Path.Combine(backupDir, "PSGAME", "0000000000000000", titleId, "sce_sys");
pgameFolder = Path.Combine(SettingsReader.Ps1Folder, "0000000000000000", titleId, "game");
pgameFolderLicense = Path.Combine(SettingsReader.Ps1Folder, "0000000000000000", titleId, "license");
scesys = Path.Combine(SettingsReader.Ps1Folder, "0000000000000000", titleId, "sce_sys");
}
}
Directory.CreateDirectory(pgameFolder);
Directory.CreateDirectory(pgameFolderl);
Directory.CreateDirectory(pgameFolderLicense);
Directory.CreateDirectory(scesys);
string psvimgFilepathl = Path.Combine(pgameFolderl, "license.psvimg");
string psvimgFilepathLicense = Path.Combine(pgameFolderLicense, "license.psvimg");
string psvimgFilepath = Path.Combine(pgameFolder, "game.psvimg");
string psvmdFilepathl = Path.Combine(pgameFolderl, "license.psvmd");
string psvmdFilepathLicense = Path.Combine(pgameFolderLicense, "license.psvmd");
string psvmdFilepath = Path.Combine(pgameFolder, "game.psvmd");
FileStream gamePsvimg = File.OpenWrite(psvimgFilepath);
gamePsvimg.SetLength(0);
PSVIMGBuilder builder = new PSVIMGBuilder(gamePsvimg, CmaKey);
foreach (string entry in entrys)
await Task.Run(() =>
{
string relativePath = entry.Remove(0, gameFolder.Length);
relativePath = relativePath.Replace('\\', '/');
if(Path.GetExtension(entry).ToUpperInvariant() == ".EDAT")
using (FileStream gamePsvimg = File.Open(psvimgFilepath, FileMode.Create, FileAccess.ReadWrite))
{
string edatContentId = GetContentIdFromPspEdat(entry);
string rifPath = Path.Combine(driveLetterSrc.Text, pspFolder.Text, "LICENSE", edatContentId + ".RIF");
if (!licenseFiles.Contains(rifPath) && File.Exists(rifPath))
licenseFiles.Add(rifPath);
}
bool isDir = File.GetAttributes(entry).HasFlag(FileAttributes.Directory);
if (isDir)
{
builder.AddDir(entry, parentPath, relativePath);
}
else
{
builder.AddFileAsync(entry, parentPath, relativePath);
while (!builder.HasFinished)
PSVIMGBuilder builder = new PSVIMGBuilder(gamePsvimg, CmaKey);
builder.RegisterCallback((ProgressInfo inf) =>
{
try
{
Invoke((Action)delegate {
int tBlocks = builder.BlocksWritten;
if (tBlocks > noBlocks) tBlocks = noBlocks;
progressBar.Value = tBlocks;
decimal progress = Math.Floor(((decimal)tBlocks / (decimal)noBlocks) * 100);
progressStatus.Text = progress.ToString() + "%";
currentFile.Text = "Processing: " + Path.GetFileName(entry);
currentFile.Text = inf.CurrentProcess;
});
});
foreach (string entry in entrys)
{
string relativePath = entry.Remove(0, gameFolder.Length);
relativePath = relativePath.Replace('\\', '/');
if (Path.GetExtension(entry).ToUpperInvariant() == ".EDAT")
{
string edatContentId = GetContentIdFromPspEdat(entry);
string rifPath = Path.Combine(pspDir, "LICENSE", edatContentId + ".RIF");
if (!licenseFiles.Contains(rifPath) && File.Exists(rifPath))
licenseFiles.Add(rifPath);
}
catch (Exception) { }
Application.DoEvents();
bool isDir = File.GetAttributes(entry).HasFlag(FileAttributes.Directory);
if (isDir)
{
builder.AddDir(entry, parentPath, relativePath);
}
else
{
builder.AddFile(entry, parentPath, relativePath);
}
}
// add __sce_ebootpbp
if(!isDlc)
builder.AddFile(EbootSig, parentPath, "/__sce_ebootpbp");
long ContentSize = builder.Finish();
using (FileStream gamePsvmd = File.Open(psvmdFilepath, FileMode.Create, FileAccess.ReadWrite))
PSVMDBuilder.CreatePsvmd(gamePsvmd, gamePsvimg, ContentSize, "game", CmaKey);
}
}
long ContentSize = builder.Finish();
gamePsvimg = File.OpenRead(psvimgFilepath);
FileStream gamePsvmd = File.OpenWrite(psvmdFilepath);
PSVMDBuilder.CreatePsvmd(gamePsvmd, gamePsvimg, ContentSize, "game", CmaKey);
gamePsvmd.Close();
gamePsvimg.Close();
// Package LICENSE
FileStream licensePsvimg = File.OpenWrite(psvimgFilepathl);
licensePsvimg.SetLength(0);
builder = new PSVIMGBuilder(licensePsvimg, CmaKey);
foreach(string licenseFile in licenseFiles)
builder.AddFile(licenseFile, "ux0:pspemu/temp/game/PSP/LICENSE", "/" + Path.GetFileNameWithoutExtension(licenseFile) + ".rif");
ContentSize = builder.Finish();
licensePsvimg = File.OpenRead(psvimgFilepathl);
FileStream licensePsvmd = File.OpenWrite(psvmdFilepathl);
PSVMDBuilder.CreatePsvmd(licensePsvmd, licensePsvimg, ContentSize, "license", CmaKey);
licensePsvmd.Close();
licensePsvimg.Close();
// Package LICENSE
using (FileStream licensePsvimg = File.Open(psvimgFilepathLicense, FileMode.Create, FileAccess.ReadWrite))
{
PSVIMGBuilder builder = new PSVIMGBuilder(licensePsvimg, CmaKey);
foreach (string licenseFile in licenseFiles)
builder.AddFile(licenseFile, "ux0:pspemu/temp/game/PSP/LICENSE", "/" + Path.GetFileNameWithoutExtension(licenseFile) + ".rif");
long ContentSize = builder.Finish();
using (FileStream licensePsvmd = File.Open(psvmdFilepathLicense, FileMode.Create, FileAccess.ReadWrite))
PSVMDBuilder.CreatePsvmd(licensePsvmd, licensePsvimg, ContentSize, "license", CmaKey);
}
});
// Write PARAM.SFO & ICON0.PNG
byte[] ParamSfo = GetSfo(ebootFile);

View File

@ -1,64 +1,4 @@
<?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.
-->
<root>
<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">

View File

@ -1,16 +1,6 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CHOVY-TRANSFER")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CHOVY-TRANSFER")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -21,16 +11,3 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b4cad2c0-ba54-46b6-a8d0-43a9c2390d3c")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net6.0-windows\publish\win-x86\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net6.0-windows</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<History>True|2023-07-10T03:53:35.4493264Z;True|2023-07-10T15:48:49.3356830+12:00;True|2023-07-10T15:44:35.4847823+12:00;True|2023-07-10T15:42:39.5955955+12:00;True|2023-07-10T15:30:22.5199359+12:00;True|2023-07-10T13:56:34.8005938+12:00;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@ -19,7 +19,7 @@ namespace CHOVY_TRANSFER.Properties {
// 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", "4.0.0.0")]
[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 {

View File

@ -12,7 +12,7 @@ namespace CHOVY_TRANSFER.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.0.1.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.4.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

257
CHOVY-TRANSFER/Sfo.cs Normal file
View File

@ -0,0 +1,257 @@
using Li.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.IO;
using System.Runtime.CompilerServices;
// A Sfo Parser Written by Li
// Because all the others are overly-complicated for no reason!
// MIT Licensed.
namespace Param
{
public class Sfo
{
private struct SfoEntry
{
internal string keyName;
internal byte type;
internal UInt32 valueSize;
internal UInt32 totalSize;
internal byte align;
internal object value;
}
const int SFO_MAGIC = 0x46535000;
const byte PSF_TYPE_BIN = 0;
const byte PSF_TYPE_STR = 2;
const byte PSF_TYPE_VAL = 4;
private Dictionary<string, SfoEntry> sfoEntries;
public Object this[string index]
{
get
{
if (sfoEntries.ContainsKey(index))
return sfoEntries[index].value;
else
return null;
}
set
{
if (sfoEntries.ContainsKey(index))
{
SfoEntry sfoEnt = sfoEntries[index];
sfoEnt.value = value;
// update sz
sfoEnt.valueSize = getObjectSz(sfoEnt.value);
if (sfoEnt.valueSize > sfoEnt.totalSize)
sfoEnt.totalSize = Convert.ToUInt32(MathUtil.CalculatePaddingAmount(Convert.ToInt32(sfoEnt.valueSize), sfoEnt.align));
// update type
sfoEnt.type = getPsfType(sfoEnt.value);
sfoEntries[index] = sfoEnt;
}
else
{
UInt32 sz = getObjectSz(value);
int alg = MathUtil.CalculatePaddingAmount(Convert.ToInt32(sz), 4);
AddKey(index, value, Convert.ToUInt32(sz + alg), 4);
}
}
}
public void AddKey(string keyName, object value, UInt32 totalSize, byte align = 4)
{
SfoEntry ent = new SfoEntry();
ent.keyName = keyName;
ent.type = getPsfType(value);
ent.valueSize = getObjectSz(value);
ent.totalSize = Convert.ToUInt32(totalSize + MathUtil.CalculatePaddingAmount(Convert.ToInt32(totalSize), align));
ent.align = align;
ent.value = value;
sfoEntries[ent.keyName] = ent;
}
public Sfo()
{
sfoEntries = new Dictionary<string, SfoEntry>();
}
private static UInt32 getObjectSz(Object obj)
{
if (obj is Int32) return 4;
if (obj is UInt32) return 4;
if (obj is String) return Convert.ToUInt32((obj as String).Length + 1);
if (obj is Byte[]) return Convert.ToUInt32((obj as Byte[]).Length);
throw new Exception("Object is of unsupported type: " + obj.GetType());
}
private static byte getPsfType(Object obj)
{
if (obj is Int32 || obj is UInt32) return PSF_TYPE_VAL;
if (obj is String) return PSF_TYPE_STR;
if (obj is Byte[]) return PSF_TYPE_BIN;
throw new Exception("Object is of unsupported type: " + obj.GetType());
}
public byte[] WriteSfo(UInt32 version = 0x101, Byte align = 0x4)
{
using (MemoryStream sfoStream = new MemoryStream())
{
WriteSfo(sfoStream, version, align);
byte[] sfoBytes = sfoStream.ToArray();
return sfoBytes;
}
}
public void WriteSfo(Stream SfoStream, UInt32 version = 0x101, Byte align = 0x4)
{
using (MemoryStream sfoStream = new MemoryStream())
{
StreamUtil sfoUtil = new StreamUtil(sfoStream);
sfoUtil.WriteUInt32(SFO_MAGIC);
sfoUtil.WriteUInt32(version);
sfoUtil.WriteUInt32(0xFFFFFFFF); // key offset
sfoUtil.WriteUInt32(0xFFFFFFFF); // value offset
// (will fill these in after the file is created)
sfoUtil.WriteInt32(sfoEntries.Count);
using (MemoryStream keyTable = new MemoryStream())
{
StreamUtil keyUtils = new StreamUtil(keyTable);
using (MemoryStream valueTable = new MemoryStream())
{
StreamUtil valueUtils = new StreamUtil(valueTable);
foreach (SfoEntry entry in sfoEntries.Values)
{
// write name
sfoUtil.WriteUInt16(Convert.ToUInt16(keyTable.Position));
keyUtils.WriteCStr(entry.keyName);
// write entry
sfoUtil.WriteByte(align); // align
sfoUtil.WriteByte(entry.type); // type
sfoUtil.WriteUInt32(entry.valueSize); // valueSize
sfoUtil.WriteUInt32(entry.totalSize); // totalSize
// write data
sfoUtil.WriteUInt32(Convert.ToUInt32(valueTable.Position)); // dataOffset
switch (entry.type)
{
case PSF_TYPE_VAL:
valueUtils.WriteUInt32(Convert.ToUInt32(entry.value));
valueUtils.WritePadding(0x00, Convert.ToInt32(entry.totalSize - entry.valueSize));
break;
case PSF_TYPE_STR:
valueUtils.WriteStrWithPadding(entry.value as String, 0x00, Convert.ToInt32(entry.totalSize));
break;
case PSF_TYPE_BIN:
valueUtils.WriteBytesWithPadding(entry.value as Byte[], 0x00, Convert.ToInt32(entry.totalSize));
break;
}
}
keyUtils.AlignTo(0x00, align);
UInt32 keyOffset = Convert.ToUInt32(sfoStream.Position);
keyTable.Seek(0x00, SeekOrigin.Begin);
keyTable.CopyTo(sfoStream);
UInt32 valueOffset = Convert.ToUInt32(sfoStream.Position);
valueTable.Seek(0x00, SeekOrigin.Begin);
valueTable.CopyTo(sfoStream);
sfoStream.Seek(0x8, SeekOrigin.Begin);
sfoUtil.WriteUInt32(keyOffset); // key offset
sfoUtil.WriteUInt32(valueOffset); // value offset
}
}
sfoStream.Seek(0x0, SeekOrigin.Begin);
sfoStream.CopyTo(SfoStream);
}
}
public static Sfo ReadSfo(Stream SfoStream)
{
Sfo sfoFile = new Sfo();
StreamUtil DataUtils = new StreamUtil(SfoStream);
// Read Sfo Header
UInt32 magic = DataUtils.ReadUInt32();
UInt32 version = DataUtils.ReadUInt32();
UInt32 keyOffset = DataUtils.ReadUInt32();
UInt32 valueOffset = DataUtils.ReadUInt32();
UInt32 count = DataUtils.ReadUInt32();
if (magic == SFO_MAGIC) //\x00PSF
{
for (int i = 0; i < count; i++)
{
SfoEntry entry = new SfoEntry();
UInt16 nameOffset = DataUtils.ReadUInt16();
entry.align = DataUtils.ReadByte();
entry.type = DataUtils.ReadByte();
entry.valueSize = DataUtils.ReadUInt32();
entry.totalSize = DataUtils.ReadUInt32();
UInt32 dataOffset = DataUtils.ReadUInt32();
int keyLocation = Convert.ToInt32(keyOffset + nameOffset);
entry.keyName = DataUtils.ReadStringAt(keyLocation);
int valueLocation = Convert.ToInt32(valueOffset + dataOffset);
switch (entry.type)
{
case PSF_TYPE_STR:
entry.value = DataUtils.ReadStringAt(valueLocation);
break;
case PSF_TYPE_VAL:
entry.value = DataUtils.ReadUInt32At(valueLocation);
break;
case PSF_TYPE_BIN:
entry.value = DataUtils.ReadBytesAt(valueLocation, Convert.ToInt32(entry.valueSize));
break;
}
sfoFile.sfoEntries[entry.keyName] = entry;
}
}
else
{
throw new InvalidDataException("Sfo Magic is Invalid.");
}
return sfoFile;
}
public static Sfo ReadSfo(byte[] Sfo)
{
using (MemoryStream SfoStream = new MemoryStream(Sfo))
{
return ReadSfo(SfoStream);
}
}
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.8.5" targetFramework="net452" />
<package id="DotNetZip" version="1.13.4" targetFramework="net452" />
</packages>

View File

@ -1,569 +0,0 @@
/* Copyright (c) 2015 - 2018 TheDarkporgramer
*
* This was originally done by Jappi88 (Jappi88 at Gmail dot com) https://github.com/Jappi88
* All modifications have been TheDarkporgramer (save sfo ext ext ) https://github.com/xXxTheDarkprogramerxXx
*
* This(software Is provided) 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications*, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledge in the product documentation is required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* *Contact must be made to discuses permission and terms.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
namespace Param_SFO
{
public class PARAM_SFO
{
#region << Enums >>
public enum DataTypes : uint
{
PSN_Game = 18248,
GameData = 0x4744,
SaveData = 0x5344,
AppPhoto = 0x4150,
AppMusic = 0x414D,
AppVideo = 0x4156,
BroadCastVideo = 0x4256,
AppleTV = 4154,
WebTV = 5754,
CellBE = 0x4342,
Home = 0x484D,
StoreFronted = 0x5346,
HDDGame = 0x4847,
DiscGame = 0x4447,
AutoInstallRoot = 0x4152,
DiscPackage = 0x4450,
ExtraRoot = 0x5852,
VideoRoot = 0x5652,
ThemeRoot = 0x5452,
DiscMovie = 0x444D,
Game_Digital_Application = 0x4081AC0,//GD
PS4_Game_Application_Patch = 28775,
Additional_Content = 25441,//PSvita PS4
GameContent = 25447,//PSVITA
Blu_Ray_Disc = 25698,//PS4
None
}
public enum FMT : ushort
{
UTF_8 = 0x0004,
ASCII = 0x0402,
Utf8Null = 0x0204,
UINT32 = 0x0404,
}
#endregion << Enums >>
#region << Vars>>
public List<Table> Tables { get; set; }
#endregion << Vars>>
#region << Example Of Calling Functions >>
//ypu can use this as SFO.Atribute
public string Attribute
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
{
if (t.Name == "ATTRIBUTE")
return t.Value;
}
return "";
}
}
public DataTypes DataType
{
get
{
if (Tables == null)
return DataTypes.None;
foreach (Table t in Tables)
if (t.Name == "CATEGORY")
return ((DataTypes)BitConverter.ToUInt16(Encoding.UTF8.GetBytes(t.Value), 0));
return DataTypes.None;
}
}
public string APP_VER
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
{
if (t.Name == "APP_VER")
return t.Value;
}
return "";
}
}
public string Detail
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
if (t.Name == "DETAIL")
return t.Value;
return "";
}
}
public string ContentID
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
if (t.Name == "CONTENT_ID")
return t.Value;
return "";
}
}
public string GetValue(string tagName)
{
foreach (Table t in Tables)
if (t.Name == tagName)
return t.Value;
return "";
}
public string TITLEID
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
if (t.Name == "TITLE_ID")
return t.Value;
return "";
}
}
public string TitleID
{
get
{
string name = TITLEID;
if (name == "")
return "";
return name.Split('-')[0];
}
}
public string Title
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
if (t.Name == "TITLE")
return t.Value;
return "";
}
}
public string Category
{
get
{
if (Tables == null)
return "";
foreach (Table t in Tables)
if (t.Name == "CATEGORY")
return t.Value;
return "";
}
}
public enum Playstation
{
ps3 = 0,
psvita = 1,
ps4 = 2,
psp = 3,
unknown = 4,//there will be a time i no longer support the scene this will be for ps5+ most probabbly
}
public Playstation PlaystationVersion
{
get
{
if (Tables == null)
return Playstation.unknown;
foreach (Table t in Tables)
{
if (t.Name == "PS3_SYSTEM_VER")
return Playstation.ps3;//this is the unique offset for ps3
if (t.Name == "PSP2_SYSTEM_VER")
{
return Playstation.psvita;//this is the only flag that tells us its a psvita
}
if (t.Name == "PSP_SYSTEM_VER")
{
return Playstation.psp;//this is how we know its a psp
}
if (t.Name == "SYSTEM_VER")//i believe this to be only ps4
{
return Playstation.ps4;
}
}
return Playstation.unknown;
}
}
#endregion << Example Of Calling Functions >>
#region Param.SFO Struct
public struct Header
{
public static byte[] Magic = { 0, 0x50, 0x53, 0x46 };
public static byte[] version = { 01, 01, 0, 0 };
public static uint KeyTableStart = 0;
public static uint DataTableStart = 0;
public static uint IndexTableEntries = 0;
private static byte[] Buffer
{
get
{
var header = new byte[20];
Array.Copy(Magic, 0, header, 0, 4);
Array.Copy(version, 0, header, 4, 4);
Array.Copy(BitConverter.GetBytes(KeyTableStart), 0, header, 8, 4);
Array.Copy(BitConverter.GetBytes(DataTableStart), 0, header, 12, 4);
Array.Copy(BitConverter.GetBytes(IndexTableEntries), 0, header, 16, 4);
return header;
}
}
public static void Read(BinaryReader input)
{
input.BaseStream.Seek(0, SeekOrigin.Begin);
input.Read(Magic, 0, 4);
input.Read(version, 0, 4);
KeyTableStart = input.ReadUInt32();
DataTableStart = input.ReadUInt32();
IndexTableEntries = input.ReadUInt32();
}
}
[Serializable]
public struct Table : IComparable
{
public index_table Indextable;
public string Name;
public string Value;
public int index;
public byte[] NameBuffer
{
get
{
var buffer = new byte[Name.Length + 1];
Array.Copy(Encoding.UTF8.GetBytes(Name), 0, buffer, 0, Encoding.UTF8.GetBytes(Name).Length);
return buffer;
}
}
public byte[] ValueBuffer
{
get
{
byte[] buffer;
switch (Indextable.param_data_fmt)
{
case FMT.ASCII:
buffer = new byte[Indextable.param_data_max_len];
Array.Copy(Encoding.ASCII.GetBytes(Value), 0, buffer, 0, Encoding.UTF8.GetBytes(Value).Length);
return buffer;
case FMT.UINT32:
return BitConverter.GetBytes(uint.Parse(Value));
case FMT.UTF_8:
buffer = new byte[Indextable.param_data_max_len];
Array.Copy(Encoding.UTF8.GetBytes(Value), 0, buffer, 0, Encoding.UTF8.GetBytes(Value).Length);
return buffer;
case FMT.Utf8Null:
buffer = new byte[Indextable.param_data_max_len];
Array.Copy(Encoding.UTF8.GetBytes(Value), 0, buffer, 0, Encoding.UTF8.GetBytes(Value).Length);/*write the length of the array*/
return buffer;
default:
return null;
}
}
}
public int CompareTo(object obj)
{
throw new NotImplementedException();
}
}
[Serializable]
public struct index_table
{
public FMT param_data_fmt; /* param_data data type */
public uint param_data_len; /* param_data used bytes */
public uint param_data_max_len; /* param_data total reserved bytes */
public uint param_data_offset; /* param_data offset (relative to start offset of data_table) */
public ushort param_key_offset; /* param_key offset (relative to start offset of key_table) */
private byte[] Buffer
{
get
{
var data = new byte[16];
Array.Copy(BitConverter.GetBytes(param_key_offset), 0, data, 0, 2);
Array.Copy(BitConverter.GetBytes(((ushort)param_data_fmt)), 0, data, 2, 2);
Array.Copy(BitConverter.GetBytes(param_data_len), 0, data, 4, 4);
Array.Copy(BitConverter.GetBytes(param_data_max_len), 0, data, 8, 4);
Array.Copy(BitConverter.GetBytes(param_data_offset), 0, data, 12, 4);
return data;
}
}
public void Read(BinaryReader input)
{
param_key_offset = input.ReadUInt16();
param_data_fmt = (FMT)input.ReadUInt16();
param_data_len = input.ReadUInt32();
param_data_max_len = input.ReadUInt32();
param_data_offset = input.ReadUInt32();
}
}
[Serializable]
private enum DATA_TYPE : byte
{
BinaryData = 0,
Utf8Text = 2,
Si32Integer = 4
}
#endregion Param.SFO Struct
#region << Methods >>
public PARAM_SFO()
{
}
public PARAM_SFO(string filepath)
{
Init(new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
}
public PARAM_SFO(byte[] inputdata)
{
Init(new MemoryStream(inputdata));
}
public PARAM_SFO(Stream input)
{
Init(input);
}
/// <summary>
/// This is the SaveSFO Function for PS3/PS4/PSVita/And PSP no longer needed for Sony's CMD
/// </summary>
/// <param name="psfo">SFO That has been opened</param>
/// <param name="filename">Save Location</param>
public void SaveSFO(PARAM_SFO psfo, string filename)
{
//we start by opening the stream to the file
using (var stream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read))
{
if (!stream.CanSeek)
throw new ArgumentException("Stream must be seekable");//throw this error we cant seek the stream
var utf8 = new UTF8Encoding(false);//encoding
using (var writer = new BinaryWriter(stream, utf8))//start binary reader
{
#region << Header Info (DevWiki) >>
/*
Header
* 0x00 0x04 magic PSF
0x04 0x04 version 01 01 00 00 1.01
0x08 0x04 key_table_start 24 00 00 00 Absolute start offset of key_table = 0x24
0x0C 0x04 data_table_start 30 00 00 00 Absolute start offset of data_table = 0x30
0x10 0x04 tables_entries 01 00 00 00 Number of entries in index_table, key_table, and data_table = 1
*/
#endregion <<Header Info >>
//so lets start writing the info
writer.Write(Header.Magic);//write magic "\0PSF"
writer.Write(Header.version);//write version info this is mayjor and minor (01 01 00 00 1.01)
Header.KeyTableStart = 0x14 + Header.IndexTableEntries * 0x10;/*we can write all this lovely info from the tables back*/
writer.Write(Header.KeyTableStart);
Header.DataTableStart = Convert.ToUInt32(Header.KeyTableStart + Tables.Sum(i => i.Name.Length + 1));//needs to be Uint
if (Header.DataTableStart % 4 != 0)
Header.DataTableStart = (Header.DataTableStart / 4 + 1) * 4;
writer.Write(Header.DataTableStart);
Header.IndexTableEntries = Convert.ToUInt32(Tables.Count);
writer.Write(Header.IndexTableEntries);
int lastKeyOffset = Convert.ToInt32(Header.KeyTableStart);
int lastValueOffset = Convert.ToInt32(Header.DataTableStart);
for (var i = 0; i < Tables.Count; i++)
{
var entry = Tables[i];
writer.BaseStream.Seek(0x14 + i * 0x10, SeekOrigin.Begin);
writer.Write((ushort)(lastKeyOffset - Header.KeyTableStart));
writer.Write((ushort)entry.Indextable.param_data_fmt);
writer.Write(entry.Indextable.param_data_len);
writer.Write(entry.Indextable.param_data_max_len);
writer.Write(lastValueOffset - Header.DataTableStart);
writer.BaseStream.Seek(lastKeyOffset, SeekOrigin.Begin);
writer.Write(utf8.GetBytes(entry.Name));
writer.Write((byte)0);
lastKeyOffset = (int)writer.BaseStream.Position;
writer.BaseStream.Seek(lastValueOffset, SeekOrigin.Begin);
writer.Write(entry.ValueBuffer);
lastValueOffset = (int)writer.BaseStream.Position;
}
//I'm doing this to just rewrite the first item (Some Cleanup will be needed)
//Or maybe not as when I checked this gives a 1 - 1 match with how the Sony tool works
//we need to rewrite that first item (PS4/PS3/PSV should be APP-VER)
lastKeyOffset = Convert.ToInt32(Header.KeyTableStart);
lastValueOffset = Convert.ToInt32(Header.DataTableStart);
var tableentry = Tables[0];
writer.BaseStream.Seek(lastKeyOffset, SeekOrigin.Begin);
writer.Write(utf8.GetBytes(tableentry.Name));
writer.Write((byte)0);
lastKeyOffset = (int)writer.BaseStream.Position;
}
}
}
private string ReadValue(BinaryReader br, index_table table)
{
br.BaseStream.Position = ((Header.DataTableStart) + table.param_data_offset);
switch (table.param_data_fmt)
{
case FMT.ASCII:
//return Encoding.GetEncoding(1252).GetString(br.ReadBytes((int) table.param_data_max_len)).Replace("\0", "");
return Encoding.UTF8.GetString(br.ReadBytes((int)table.param_data_max_len)).Replace("\0", "");
case FMT.UINT32:
return br.ReadUInt32().ToString();
case FMT.UTF_8:
return Encoding.UTF8.GetString(br.ReadBytes((int)table.param_data_max_len)).Replace("\0", "");
case FMT.Utf8Null:
return Encoding.UTF8.GetString(br.ReadBytes((int)table.param_data_max_len)).Replace("\0", "");
default:
return null;
}
}
private string ReadValueSpecialChars(BinaryReader br, index_table table)
{
br.BaseStream.Position = ((Header.DataTableStart) + table.param_data_offset);
switch (table.param_data_fmt)
{
case FMT.ASCII:
return Encoding.UTF8.GetString(br.ReadBytes((int)table.param_data_max_len)).Replace("\0", "");
case FMT.UINT32:
return br.ReadUInt32().ToString();
case FMT.UTF_8:
return Encoding.UTF8.GetString(br.ReadBytes((int)table.param_data_max_len)).Replace("\0", "");
default:
return null;
}
}
private string ReadName(BinaryReader br, index_table table)
{
br.BaseStream.Position = (Header.KeyTableStart + table.param_key_offset);
string name = "";
while (((byte)br.PeekChar()) != 0)
name += br.ReadChar();
br.BaseStream.Position++;
return name;
}
/// <summary>
/// Start Reading the Parameter file
/// </summary>
/// <param name="input">Input Stream</param>
private void Init(Stream input)
{
using (var br = new BinaryReader(input))
{
Header.Read(br);
var tables = new List<index_table>();
for (int i = 0; i < Header.IndexTableEntries; i++)
{
var t = new index_table();
t.Read(br);
tables.Add(t);
}
var xtables = new List<Table>();
int count = 0;
foreach (index_table t in tables)
{
var x = new Table();
x.index = count;
x.Indextable = t;
x.Name = ReadName(br, t);
x.Value = ReadValue(br, t);
count++;
xtables.Add(x);
}
Tables = xtables;
br.Close();
}
}
#endregion << Methods >>
}
}

9
LiLib/LiLib.csproj Normal file
View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

32
LiLib/MathUtil.cs Normal file
View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Li.Utilities
{
public static class MathUtil
{
public static int CalculateDifference(int val1, int val2)
{
int smaller = Convert.ToInt32(Math.Min(val1, val2));
int larger = Convert.ToInt32(Math.Max(val1, val2));
return larger - smaller;
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).ToArray();
}
public static int CalculatePaddingAmount(int total, int alignTo)
{
int remainder = total % alignTo;
int padAmt = alignTo - (remainder);
if ((remainder) == 0) return 0;
return padAmt;
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Li.Progress
{
public class ProgressInfo
{
private int totalDone;
private int totalRemain;
private string currentlyDoing;
public int Done
{
get
{
return totalDone;
}
}
public int Remain
{
get
{
return totalRemain;
}
}
public string CurrentProcess
{
get
{
return currentlyDoing;
}
}
public double Progress
{
get
{
return Convert.ToDouble(totalDone) / Convert.ToDouble(totalRemain) * 100.0;
}
}
public int ProgressInt
{
get
{
return Convert.ToInt32(Math.Floor(Progress));
}
}
internal ProgressInfo(int done, int remain, string currentProcess)
{
totalDone = done;
totalRemain = remain;
currentlyDoing = currentProcess;
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Li.Progress
{
public class ProgressTracker
{
private List<Action<ProgressInfo>> progressCallbacks = new List<Action<ProgressInfo>>();
public void RegisterCallback(Action<ProgressInfo> cb)
{
progressCallbacks.Add(cb);
}
protected void copyToProgress(Stream src, Stream dst, string msg)
{
src.Seek(0, SeekOrigin.Begin);
byte[] readBuffer = new byte[0x30000];
while (src.Position < src.Length)
{
int readAmt = src.Read(readBuffer, 0x00, readBuffer.Length);
dst.Write(readBuffer, 0x00, readAmt);
updateProgress(Convert.ToInt32(src.Position), Convert.ToInt32(src.Length), msg);
}
}
protected void updateProgress(int done, int remain, string what)
{
ProgressInfo inf = new ProgressInfo(done, remain, what);
foreach (Action<ProgressInfo> cb in progressCallbacks)
cb(inf);
}
}
}

204
LiLib/StreamUtil.cs Normal file
View File

@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Li.Utilities
{
public class StreamUtil
{
private Stream s;
public StreamUtil(Stream s)
{
this.s = s;
}
public string ReadStrLen(int len)
{
return Encoding.UTF8.GetString(ReadBytes(len));
}
public string ReadCDStr(int len)
{
return ReadStrLen(len).Trim(' ');
}
public string ReadCStr()
{
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
byte c = (byte)s.ReadByte();
if (c == 0)
break;
ms.WriteByte(c);
}
return Encoding.UTF8.GetString(ms.ToArray());
}
}
public UInt32 ReadUInt32At(int location)
{
long oldPos = s.Position;
s.Seek(location, SeekOrigin.Begin);
UInt32 outp = ReadUInt32();
s.Seek(oldPos, SeekOrigin.Begin);
return outp;
}
public Int32 ReadInt32At(int location)
{
long oldPos = s.Position;
s.Seek(location, SeekOrigin.Begin);
Int32 outp = ReadInt32();
s.Seek(oldPos, SeekOrigin.Begin);
return outp;
}
public byte[] ReadBytesAt(int location, int length)
{
long oldPos = s.Position;
s.Seek(location, SeekOrigin.Begin);
byte[] work_buf = ReadBytes(length);
s.Seek(oldPos, SeekOrigin.Begin);
return work_buf;
}
public string ReadStringAt(int location)
{
long oldPos = s.Position;
s.Seek(location, SeekOrigin.Begin);
string outp = ReadCStr();
s.Seek(oldPos, SeekOrigin.Begin);
return outp;
}
public byte ReadByte()
{
return (byte)s.ReadByte();
}
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 WriteBytesWithPadding(byte[] data, byte b, int len)
{
if (len < data.Length)
{
s.Write(data, 0, len);
return;
}
else
{
WriteBytes(data);
WritePadding(b, (len - data.Length));
}
}
public void WriteStrWithPadding(string str, byte b, int len)
{
WriteBytesWithPadding(Encoding.UTF8.GetBytes(str), b, len);
}
public void WriteUInt64(UInt64 v)
{
WriteBytes(BitConverter.GetBytes(v));
}
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 WriteInt32BE(Int32 v)
{
WriteBytes(BitConverter.GetBytes(v).Reverse().ToArray());
}
public void WriteInt32At(Int32 v, long location)
{
long oldPos = s.Position;
s.Seek(location, SeekOrigin.Begin);
WriteInt32(v);
s.Seek(oldPos, SeekOrigin.Begin);
}
public void WriteUInt32At(UInt32 v, long location)
{
long oldPos = s.Position;
s.Seek(location, SeekOrigin.Begin);
WriteUInt32(v);
s.Seek(oldPos, SeekOrigin.Begin);
}
public void WriteInt32(Int32 v)
{
WriteBytes(BitConverter.GetBytes(v));
}
public void WriteCStr(string str)
{
WriteStr(str);
WriteByte(0x00);
}
public void WriteStr(string str)
{
WriteBytes(Encoding.UTF8.GetBytes(str));
}
public void WritePadding(byte b, int len)
{
if (len < 0) return;
for(int i = 0; i < len; i++)
{
WriteByte(b);
}
}
public void AlignTo(byte padByte, int align)
{
int padAmt = MathUtil.CalculatePaddingAmount(Convert.ToInt32(s.Position), align);
this.WritePadding(padByte, padAmt);
}
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);
}
}
}

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 MemoryMarshal.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 MemoryMarshal.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 MemoryMarshal.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 MemoryMarshal.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 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;
}
}
}

251
PspCrypto/AesHelper.cs Normal file
View File

@ -0,0 +1,251 @@
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 MemoryMarshal.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 MemoryMarshal.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 = MemoryMarshal.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 = MemoryMarshal.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;
}
}
}

113
PspCrypto/ECDsaHelper.cs Normal file
View File

@ -0,0 +1,113 @@
using PspCrypto.Security.Cryptography;
using System;
using System.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 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);
}
}
}

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

41
PspCrypto/Lz.cs Normal file
View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
namespace PspCrypto
{
public class Lz
{
public static byte[] compress(byte[] in_buf, bool np9660=false)
{
//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(np9660);
// compress data, and get the compressed data length
return lzrc.lzrc_compress(in_buf, in_buf.Length);
}
public static int decompress(byte[] @out, byte[] @in, int size, int insize, bool np9660=false)
{
//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(np9660);
lzrc.lzrc_decompress(@out, size, @in, insize);
return 0;
}
}
}

908
PspCrypto/Lzrc.cs Normal file
View File

@ -0,0 +1,908 @@
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
namespace PspCrypto
{
public class Lzrc
{
private bool np9660;
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;
private byte[][] bm_dist_bits;
private byte[][] bm_dist;
private byte[][] bm_match;
private byte[][] bm_len;
const int MAX_WIN_SZ = 16384;
const int MAX_TBL_SZ = 65280;
const int TBL_SZ = 65536;
private byte[] text_buf = new byte[TBL_SZ];
private int t_start, t_end, t_fill, sp_fill;
private int t_len, t_pos;
private int[] prev = new int[TBL_SZ], next = new int[TBL_SZ];
private int[] root = new int[TBL_SZ];
public Lzrc(bool np9660 = false)
{
this.np9660 = np9660;
if (np9660)
{
this.bm_literal = new byte[8][];
this.bm_dist_bits = new byte[8][];
this.bm_dist = new byte[18][];
this.bm_match = new byte[8][];
this.bm_len = new byte[8][];
}
else
{
this.bm_literal = new byte[8][];
this.bm_dist_bits = new byte[8][];
this.bm_dist = new byte[16][];
this.bm_match = new byte[8][];
this.bm_len = new byte[8][];
}
}
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[] in_buf, int in_len)
{
input = in_buf;
this.in_len = in_len;
in_ptr = 0;
this.output = new byte[in_len];
this.out_len = in_len;
out_ptr = 0;
range = 0xffffffff;
code = 0x00000000;
lc = 5;
out_code = 0xffffffff;
re_putbyte(lc);
if (this.np9660)
{
Init(bm_literal, 0x80, 256);
Init(bm_dist_bits, 0x80, 39);
Init(bm_dist, 0x80, 8);
Init(bm_match, 0x80, 8);
Init(bm_len, 0x80, 31);
}
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
}
//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;
if (this.np9660)
{
Init(bm_literal, 0x80, 256);
Init(bm_dist_bits, 0x80, 39);
Init(bm_dist, 0x80, 8);
Init(bm_match, 0x80, 8);
Init(bm_len, 0x80, 31);
}
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
}
}
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];
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];
}
if (this.np9660)
{
// have we calculated match_dist of 256 when its not the end?
if (t_len == 256 && in_ptr < in_len)
return 1;
}
if (t_pos < 0) throw new Exception("t_pos was < 0 :?"); // TODO: figure out why this happens on np9660.
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 == MAX_WIN_SZ)
{
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);
}
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)
{
out_len += 0x1000;
Array.Resize(ref output, out_len);
}
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));
}
private byte[] re_trunc()
{
Array.Resize(ref output, out_ptr);
return output;
}
public byte[] lzrc_compress(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;
// initalize buffers to all 0x80
re_init(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;
bool flg = false;
while (true)
{
round += 1;
match_step = 0;
fill_buffer();
insert_node(t_end, out match_len, out match_dist, 1);
if (match_len < 256)
{
// condition is different if np9660 vs pops
if (this.np9660)
flg = (match_len < 4 && match_dist > 255);
else
flg = (match_len <= 4 && match_dist > 255);
if(flg) // if (condition)
match_len = 1;
update_tree(match_len);
}
// condition is different if np9660 vs pops
if (this.np9660)
flg = (match_len == 1 || (match_len < 4 && match_dist > 255));
else
flg = (match_len == 1 || (match_len <= 4 && match_dist > 255));
if (flg)
{
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 (!this.np9660 && in_ptr >= in_len)
{
re_normalize();
re_flush();
return re_trunc();
}
}
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 (this.np9660)
flg = (match_len == 256);
else
flg = (in_ptr >= in_len);
if (flg)
{
re_normalize();
re_flush();
return re_trunc();
}
}
// determine limit ...
dist_state = 0;
limit = 8;
// condition is different if np9660 vs pops
if (this.np9660)
flg = (match_len > 3);
else
flg = (match_len > 4);
if (flg) // if (condition)
{
dist_state += 7;
if (this.np9660)
{
limit = 44;
}
else
{
limit = 16;
}
}
// 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;
bool flg = false;
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 (this.np9660 && match_len == 0xFF)
return out_ptr;
}
dist_state = 0;
limit = 8;
if (this.np9660)
flg = (match_len > 2);
else
flg = (match_len > 3);
if (flg)
{
dist_state += 7;
if (this.np9660)
limit = 44;
else
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,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="DotNetZip" Version="1.16.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", "17.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>

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

1338
PspCrypto/SceNpDrm.cs Normal file

File diff suppressed because it is too large Load Diff

28
PspCrypto/SceRtc.cs Normal file
View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PspCrypto
{
public class SceRtc
{
public static ulong ksceRtcGetCurrentTick()
{
return ksceRtcGetCurrentSecureTick();
}
public static ulong ksceRtcGetCurrentNetworkTick()
{
return ksceRtcGetCurrentSecureTick();
}
public static ulong ksceRtcGetCurrentSecureTick()
{
DateTime epoch = new DateTime(1, 1, 1, 0, 0, 0);
DateTime now = DateTime.UtcNow;
TimeSpan ts = now.Subtract(epoch);
return Convert.ToUInt64(Math.Floor(ts.TotalMilliseconds)) * 1000;
}
}
}

View File

@ -0,0 +1,170 @@
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 = new ReadOnlySpan<byte>(data, 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.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
Y = normalG.AffineYCoord.ToBigInteger().ToByteArrayUnsigned()
}
};
var parameters = new System.Security.Cryptography.ECParameters
{
Curve = curve
};
if (includePrivateParameters && _ecKeyParameters is ECPrivateKeyParameters privateKeyParameters)
{
parameters.D = privateKeyParameters.D.ToByteArrayUnsigned();
var publicKey = _ecKeyParameters.Parameters.G.Multiply(privateKeyParameters.D).Normalize();
parameters.Q = new System.Security.Cryptography.ECPoint
{
X = publicKey.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
Y = publicKey.AffineYCoord.ToBigInteger().ToByteArrayUnsigned()
};
}
else if (_ecKeyParameters is ECPublicKeyParameters publicKeyParameters)
{
var publicKey = publicKeyParameters.Q.Normalize();
parameters.Q = new System.Security.Cryptography.ECPoint
{
X = publicKey.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
Y = publicKey.AffineYCoord.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;
}
}

34
PspCrypto/Utils.cs Normal file
View File

@ -0,0 +1,34 @@
using System;
using System.Runtime.InteropServices;
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;
}
public static void BuildDrmBBMacFinal2(Span<byte> mac)
{
Span<byte> checksum = new byte[20 + 0x10];
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;
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.

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Vita.ContentManager
{
public class Account
{
public Account(UInt64 accountId)
{
this.accountId = accountId;
}
private UInt64 accountId;
public bool Devkit;
public byte[] AccountId
{
get
{
if (Devkit) return new byte[8];
return BitConverter.GetBytes(accountId);
}
}
public string AccountIdStr
{
get
{
return BitConverter.ToString(AccountId).Replace("-", "").ToLowerInvariant();
}
}
public string CmaKeyStr
{
get
{
return KeyGenerator.GenerateKeyStr(AccountIdStr);
}
}
public byte[] CmaKey
{
get
{
return KeyGenerator.GenerateKey(AccountId);
}
}
}
}

View File

@ -1,67 +1,66 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace KeyDerivation
{
class CmaKeys
{
static Byte[] Passphrase = Encoding.ASCII.GetBytes("Sri Jayewardenepura Kotte");
static Byte[] Key = { 0xA9, 0xFA, 0x5A, 0x62, 0x79, 0x9F, 0xCC, 0x4C, 0x72, 0x6B, 0x4E, 0x2C, 0xE3, 0x50, 0x6D, 0x38 };
public static string GenerateKeyStr(string Aid)
{
try
{
Int64 longlong = Convert.ToInt64(Aid, 16);
byte[] AidBytes = BitConverter.GetBytes(longlong);
Array.Reverse(AidBytes);
byte[] DerivedKey = CmaKeys.GenerateKey(AidBytes);
return BitConverter.ToString(DerivedKey).Replace("-", "");
}
catch (Exception)
{
return "INVALID_AID";
}
}
public static byte[] GenerateKey(byte[] Aid)
{
var ms = new MemoryStream();
ms.Write(Aid, 0, Aid.Length);
ms.Write(Passphrase, 0, Passphrase.Length);
Byte[] DerviedKey = ms.ToArray();
ms.Dispose();
SHA256 sha = SHA256.Create();
DerviedKey = sha.ComputeHash(DerviedKey);
sha.Dispose();
DerviedKey = Decrypt(DerviedKey, Key);
return DerviedKey;
}
private static byte[] Decrypt(byte[] cipherData,
byte[] Key)
{
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.ECB;
alg.Padding = PaddingMode.None;
alg.KeySize = 128;
alg.Key = Key;
CryptoStream cs = new CryptoStream(ms,
alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
}
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Vita.ContentManager
{
public class KeyGenerator
{
static Byte[] Passphrase = Encoding.ASCII.GetBytes("Sri Jayewardenepura Kotte");
static Byte[] Key = { 0xA9, 0xFA, 0x5A, 0x62, 0x79, 0x9F, 0xCC, 0x4C, 0x72, 0x6B, 0x4E, 0x2C, 0xE3, 0x50, 0x6D, 0x38 };
public static string GenerateKeyStr(string Aid)
{
try
{
Int64 longlong = Convert.ToInt64(Aid, 16);
byte[] AidBytes = BitConverter.GetBytes(longlong);
Array.Reverse(AidBytes);
byte[] DerivedKey = GenerateKey(AidBytes);
return BitConverter.ToString(DerivedKey).Replace("-", "");
}
catch (Exception)
{
return "INVALID_AID";
}
}
public static byte[] GenerateKey(byte[] Aid)
{
var ms = new MemoryStream();
ms.Write(Aid, 0, Aid.Length);
ms.Write(Passphrase, 0, Passphrase.Length);
Byte[] DerviedKey = ms.ToArray();
ms.Dispose();
SHA256 sha = SHA256.Create();
DerviedKey = sha.ComputeHash(DerviedKey);
sha.Dispose();
DerviedKey = Decrypt(DerviedKey, Key);
return DerviedKey;
}
private static byte[] Decrypt(byte[] cipherData, byte[] Key)
{
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.ECB;
alg.Padding = PaddingMode.None;
alg.KeySize = 128;
alg.Key = Key;
CryptoStream cs = new CryptoStream(ms,
alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
}
}

View File

@ -0,0 +1,177 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Vita.ContentManager
{
public class SettingsReader
{
private static string? overrideBackupsFolder = null;
public static string AppFolder
{
get
{
return Path.Combine(BackupsFolder, "APP");
}
}
public static string PspSavedataFolder
{
get
{
return Path.Combine(BackupsFolder, "PSAVEDATA");
}
}
public static string PsmFolder
{
get
{
return Path.Combine(BackupsFolder, "PSM");
}
}
public static string SystemFolder
{
get
{
return Path.Combine(BackupsFolder, "SYSTEM");
}
}
public static string Ps1Folder
{
get
{
return Path.Combine(BackupsFolder, "PSGAME");
}
}
public static string PspFolder
{
get
{
return Path.Combine(BackupsFolder, "PGAME");
}
}
public static string BackupsFolder
{
get
{
if (overrideBackupsFolder is not null) return overrideBackupsFolder;
string? cmaFolder = getQcmaPSVitaFolder();
if (cmaFolder is not null) return cmaFolder;
cmaFolder = getDevkitCmaPSVitaFolder();
if (cmaFolder is not null) return cmaFolder;
cmaFolder = getSonyCmaPSVitaFolder();
if (cmaFolder is not null) return cmaFolder;
return getDefaultCmaPSVitaFolder();
}
set
{
overrideBackupsFolder = value;
}
}
private static string getDefaultCmaPSVitaFolder()
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "PS Vita");
}
private static string getQcmaConfFile()
{
if (OperatingSystem.IsLinux())
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "codestation", "qcma.conf");
else if (OperatingSystem.IsMacOS())
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Preferences", "com.codestation.qcma.plist");
else
throw new PlatformNotSupportedException("cannot open qcma config as i dont know where it is.");
}
private static string? getQcmaConfigSetting(string file, string key)
{
if (!File.Exists(file)) return null;
if (OperatingSystem.IsLinux())
{
using (TextReader confFile = File.OpenText(file))
{
for (string? ln = confFile.ReadLine();
ln is not null;
ln = confFile.ReadLine())
{
ln = ln.Trim();
if (ln.StartsWith("[")) continue;
string[] kvp = ln.Split('=');
if (kvp.Length < 2) continue;
string settingKey = kvp[0].Trim();
string settingValue = kvp[1].Trim();
if (settingKey == key)
return settingValue;
}
}
}
else if (OperatingSystem.IsMacOS())
{
throw new PlatformNotSupportedException("TODO: Implement reading bplist file from mac os");
}
return null;
}
private static string? getRegistryKey(string registryPath, string keyName)
{
if (OperatingSystem.IsWindows())
{
using (RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(registryPath))
{
if (regKey is null) return null;
string? keyData = (regKey.GetValue(keyName) as string);
if (keyData is null) return null;
return keyData;
}
}
else
{
throw new PlatformNotSupportedException("cannot use registry on os other than windows");
}
}
private static string? getSonyCmaPSVitaFolder()
{
if (OperatingSystem.IsWindows())
{
return getRegistryKey(@"Software\Sony Corporation\Sony Corporation\Content Manager Assistant\Settings", "ApplicationHomePath");
}
return null;
}
private static string? getDevkitCmaPSVitaFolder()
{
if (OperatingSystem.IsWindows())
{
return getRegistryKey(@"Software\SCE\PSP2\Content Manager Assistant for PlayStation(R)Vita DevKit\Settings", "ApplicationHomePath");
}
return null;
}
private static string? getQcmaPSVitaFolder()
{
if (OperatingSystem.IsWindows())
{
return getRegistryKey(@"Software\codestation\qcma", "appsPath");
}
else if (OperatingSystem.IsLinux())
{
string qcmaConf = getQcmaConfFile();
return getQcmaConfigSetting(qcmaConf, "appsPath");
}
else if (OperatingSystem.IsMacOS())
{
string qcmaConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Preferences", "com.codestation.qcma.plist");
// TODO: read file
}
return null;
}
}
}

View File

@ -0,0 +1,350 @@
using System;
using System.IO;
using static Vita.PsvImgTools.SceIoStat;
namespace Vita.PsvImgTools
{
public class PSVIMGFileStream : Stream
{
long length = 0;
long startPos = 0;
long endPos = 0;
long position = 0;
private PSVIMGStream psvStream;
public PSVIMGFileStream(PSVIMGStream psv, string Path)
{
psvStream = psv;
if (Path.First() == '/')
findFile(Path);
else
findFileMatching(Path);
}
public void WriteToFile(string FilePath)
{
using (FileStream fs = File.OpenWrite(FilePath))
{
fs.SetLength(0);
int written = 0;
long left = length - written;
byte[] work_buf;
Seek(0x00, SeekOrigin.Begin);
while (left > 0)
{
left = length - written;
if (left < 0x10000)
{
work_buf = new byte[left];
}
else
{
work_buf = new byte[0x10000];
}
Read(work_buf, 0x00, work_buf.Length);
fs.Write(work_buf, 0x00, work_buf.Length);
written += work_buf.Length;
}
}
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override long Length
{
get
{
return length;
}
}
public override long Position
{
get
{
return position;
}
set
{
Seek(value, SeekOrigin.Begin);
}
}
public override void Flush()
{
psvStream.Flush();
}
private int _read(byte[] buffer, int offset, int count)
{
using (MemoryStream ms = new MemoryStream())
{
int read = 0;
while (true)
{
int remainBlock = Convert.ToInt32(psvStream.BlockRemaining - PSVIMGConstants.SHA256_BLOCK_SIZE);
int remainRead = count - read;
if (remainRead < remainBlock)
{
byte[] tmp = new byte[remainRead];
psvStream.Read(tmp, 0x00, remainRead);
ms.Write(tmp, 0x00, tmp.Length);
read += remainRead;
break;
}
else
{
byte[] tmp = new byte[remainBlock];
psvStream.Read(tmp, 0x00, remainBlock);
ms.Write(tmp, 0x00, tmp.Length);
psvStream.Seek(PSVIMGConstants.SHA256_BLOCK_SIZE, SeekOrigin.Current);
read += Convert.ToInt32(remainBlock);
}
}
ms.Seek(0x00, SeekOrigin.Begin);
return ms.Read(buffer, offset, count);
}
}
private long _seek(long amount, SeekOrigin orig)
{
long ToSeek = 0;
long SeekAdd = 0;
long remainBlock = psvStream.BlockRemaining - PSVIMGConstants.SHA256_BLOCK_SIZE;
while (true)
{
long remainSeek = amount - ToSeek;
if (remainSeek < remainBlock)
{
ToSeek += remainSeek;
break;
}
else
{
ToSeek += remainBlock;
SeekAdd += PSVIMGConstants.SHA256_BLOCK_SIZE;
}
remainBlock = PSVIMGConstants.PSVIMG_BLOCK_SIZE;
}
ToSeek += SeekAdd;
return psvStream.Seek(ToSeek, orig);
}
public override int Read(byte[] buffer, int offset, int count)
{
if (startPos + count > endPos)
{
count = Convert.ToInt32(endPos);
}
return _read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (origin == SeekOrigin.Begin)
{
if (offset <= endPos)
{
psvStream.Seek(startPos, SeekOrigin.Begin);
_seek(offset, SeekOrigin.Current);
position = offset;
return position;
}
else
{
throw new IndexOutOfRangeException("Offset is out of range of file");
}
}
else if (origin == SeekOrigin.Current)
{
if (offset <= endPos)
{
_seek(offset, SeekOrigin.Current);
position += offset;
return position;
}
else
{
throw new IndexOutOfRangeException("Offset is out of range of file");
}
}
else
{
long realOffset = endPos + offset;
if (realOffset <= endPos)
{
_seek(realOffset, SeekOrigin.Begin);
return Position;
}
else
{
throw new IndexOutOfRangeException("Offset is out of range of file");
}
}
}
public override void SetLength(long value)
{
throw new NotImplementedException("PSVFileStream is Read-Only");
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException("PSVFileStream is Read-Only");
}
private ushort readUInt16()
{
byte[] intBuf = new byte[0x2];
_read(intBuf, 0x00, 0x02);
return BitConverter.ToUInt16(intBuf, 0);
}
private uint readUInt32()
{
byte[] intBuf = new byte[0x4];
_read(intBuf, 0x00, 0x04);
return BitConverter.ToUInt32(intBuf, 0);
}
private ulong readUInt64()
{
byte[] intBuf = new byte[0x8];
_read(intBuf, 0x00, 0x08);
return BitConverter.ToUInt64(intBuf, 0);
}
private SceDateTime readDatetime()
{
SceDateTime dateTime = new SceDateTime();
dateTime.Year = readUInt16();
dateTime.Month = readUInt16();
dateTime.Day = readUInt16();
dateTime.Hour = readUInt16();
dateTime.Minute = readUInt16();
dateTime.Second = readUInt16();
dateTime.Microsecond = readUInt32();
return dateTime;
}
private SceIoStat readStats()
{
SceIoStat stat = new SceIoStat();
stat.Mode = (Modes)readUInt32();
stat.Attributes = (AttributesEnum)readUInt32();
stat.Size = readUInt64();
stat.CreationTime = readDatetime();
stat.AccessTime = readDatetime();
stat.ModificaionTime = readDatetime();
for (int i = 0; i < stat.Private.Length; i++)
{
stat.Private[i] = readUInt32();
}
return stat;
}
private PsvImgHeader readHeader()
{
PsvImgHeader header = new PsvImgHeader();
header.SysTime = readUInt64();
header.Flags = readUInt64();
header.Statistics = readStats();
_read(header.bParentPath, 0x00, 256);
header.unk_16C = readUInt32();
_read(header.bPath, 0x00, 256);
_read(header.Padding, 0x00, 904);
_read(header.bEnd, 0x00, 12);
return header;
}
public override void Close()
{
psvStream.Dispose();
base.Close();
}
private PsvImgTailer readTailer()
{
PsvImgTailer tailer = new PsvImgTailer();
tailer.Flags = readUInt64();
_read(tailer.Padding, 0x00, 1004);
_read(tailer.bEnd, 0x00, 12);
return tailer;
}
private void findFileMatching(string path)
{
_seek(0x00, SeekOrigin.Begin);
while (psvStream.Position < psvStream.Length)
{
PsvImgHeader header = readHeader();
long size = (long)header.Statistics.Size;
long padding = PSVIMGPadding.GetPadding(size);
if (header.Path.Contains(path, StringComparison.InvariantCultureIgnoreCase))
{
length = size;
startPos = psvStream.Position;
endPos = startPos + length;
return;
}
else
{
_seek(size + padding, SeekOrigin.Current);
PsvImgTailer tailer = readTailer();
}
}
throw new FileNotFoundException("Cannot find file specified");
}
private void findFile(string path)
{
_seek(0x00, SeekOrigin.Begin);
while (psvStream.Position < psvStream.Length)
{
PsvImgHeader header = readHeader();
long size = (long)header.Statistics.Size;
long padding = PSVIMGPadding.GetPadding(size);
if (header.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase))
{
length = size;
startPos = psvStream.Position;
endPos = startPos + length;
return;
}
else
{
_seek(size + padding, SeekOrigin.Current);
PsvImgTailer tailer = readTailer();
}
}
throw new FileNotFoundException("Cannot find file specified");
}
}
}

View File

@ -0,0 +1,365 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace Vita.PsvImgTools
{
public class PSVIMGStream : Stream
{
private Stream baseStream;
private MemoryStream blockStream;
private byte[] key;
public Stream BaseStream
{
get
{
return baseStream;
}
}
public byte[] Key
{
get
{
return key;
}
set
{
key = value;
}
}
public long BlockNo
{
get
{
return getBlockIndex();
}
set
{
seekToBlock(value);
update();
}
}
public long BlockRemaining
{
get
{
return getRemainingBlock();
}
}
public long BlockPosition
{
get
{
return blockStream.Position;
}
set
{
blockStream.Seek(value, SeekOrigin.Begin);
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override bool CanRead
{
get
{
return true;
}
}
public override long Length
{
get
{
return baseStream.Length - PSVIMGConstants.AES_BLOCK_SIZE;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override long Position
{
get
{
return baseStream.Position - PSVIMGConstants.AES_BLOCK_SIZE;
}
set
{
Seek(value, SeekOrigin.Begin);
}
}
public PSVIMGStream(Stream file, byte[] KEY)
{
baseStream = file;
key = KEY;
if (!verifyFooter())
{
throw new Exception("Invalid KEY!");
}
blockStream = new MemoryStream();
baseStream.Seek(PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Begin);
update();
}
public override int Read(byte[] buffer, int offset, int count)
{
int remaining = (int)getRemainingBlock();
int read = 0;
if (count < remaining)
{
read += blockStream.Read(buffer, offset, count);
baseStream.Seek(count, SeekOrigin.Current);
}
else
{
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
update();
remaining = (int)getRemainingBlock();
int curPos = count - read;
if (curPos > remaining)
{
read += remaining;
blockStream.CopyTo(ms, remaining);
baseStream.Seek(remaining, SeekOrigin.Current);
}
else
{
read += curPos;
blockStream.CopyTo(ms, curPos);
baseStream.Seek(curPos, SeekOrigin.Current);
break;
}
}
ms.Seek(0x00, SeekOrigin.Begin);
ms.Read(buffer, offset, count);
}
}
return read;
}
public override void Flush()
{
update();
baseStream.Flush();
blockStream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
long ret = 0;
if (origin == SeekOrigin.Begin)
{
ret = baseStream.Seek(offset + PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Begin);
}
else if (origin == SeekOrigin.Current)
{
long pos = baseStream.Position;
if (pos + offset >= PSVIMGConstants.AES_BLOCK_SIZE)
{
ret = baseStream.Seek(offset, SeekOrigin.Current);
}
else
{
ret = baseStream.Seek(offset + PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Current);
}
}
else if (origin == SeekOrigin.End)
{
long pos = baseStream.Length;
if (pos + offset >= PSVIMGConstants.AES_BLOCK_SIZE)
{
ret = baseStream.Seek(offset, SeekOrigin.End);
}
else
{
ret = baseStream.Seek(offset + PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.End);
}
}
update();
return ret;
}
public override void SetLength(long value)
{
throw new NotImplementedException("PSVIMGStream is Read-Only");
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException("PSVIMGStream is Read-Only");
}
public override void Close()
{
blockStream.Dispose();
baseStream.Dispose();
base.Close();
}
private void update()
{
long offset = Position % PSVIMGConstants.FULL_PSVIMG_SIZE;
long blockIndex = getBlockIndex();
byte[] decryptedBlock = getBlock(blockIndex);
blockStream.Seek(0x00, SeekOrigin.Begin);
blockStream.SetLength(decryptedBlock.Length);
blockStream.Write(decryptedBlock, 0x00, decryptedBlock.Length);
seekToBlock(blockIndex);
baseStream.Seek(offset, SeekOrigin.Current);
blockStream.Seek(offset, SeekOrigin.Begin);
}
private long getBlockIndex()
{
long i = 0;
long curPos = baseStream.Position;
long fullBlock;
long blockOffset;
while (true)
{
blockOffset = i * PSVIMGConstants.FULL_PSVIMG_SIZE + PSVIMGConstants.AES_BLOCK_SIZE;
long remaining = getRemainingBase();
if (remaining < PSVIMGConstants.FULL_PSVIMG_SIZE)
{
fullBlock = blockOffset + remaining;
}
else
{
fullBlock = blockOffset + PSVIMGConstants.FULL_PSVIMG_SIZE;
}
if (curPos >= blockOffset && curPos < fullBlock)
{
break;
}
if (blockOffset > baseStream.Length)
{
break;
}
i++;
}
return i;
}
private long getRemainingBase()
{
return baseStream.Length - baseStream.Position;
}
private long getRemainingBlock()
{
return blockStream.Length - blockStream.Position;
}
private byte[] getIV(long blockindex)
{
byte[] iv = new byte[0x10];
seekToBlock(blockindex);
baseStream.Seek(baseStream.Position - PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Begin);
baseStream.Read(iv, 0x00, iv.Length);
return iv;
}
private byte[] aes_cbc_decrypt(byte[] cipherData, byte[] IV)
{
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
private void seekToBlock(long blockIndex)
{
long blockOffset;
blockOffset = blockIndex * PSVIMGConstants.FULL_PSVIMG_SIZE + PSVIMGConstants.AES_BLOCK_SIZE;
if (blockOffset > baseStream.Length)
{
blockOffset = baseStream.Length;
}
baseStream.Seek(blockOffset, SeekOrigin.Begin);
}
private bool verifyFooter()
{
byte[] Footer = new byte[0x10];
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
baseStream.Seek(baseStream.Length - (Footer.Length + IV.Length), SeekOrigin.Begin);
baseStream.Read(IV, 0x00, PSVIMGConstants.AES_BLOCK_SIZE);
baseStream.Read(Footer, 0x00, 0x10);
byte[] FooterDec = aes_cbc_decrypt(Footer, IV);
ulong FooterLen;
using (MemoryStream ms = new MemoryStream(FooterDec))
{
ms.Seek(0x4, SeekOrigin.Current);
ms.Seek(0x4, SeekOrigin.Current);
byte[] LenInt = new byte[0x8];
ms.Read(LenInt, 0x00, 0x8);
FooterLen = BitConverter.ToUInt64(LenInt, 0x00);
}
if (Convert.ToUInt64(baseStream.Length) == FooterLen)
{
return true;
}
else
{
return false;
}
}
private byte[] getBlock(long blockIndex)
{
byte[] iv = getIV(blockIndex);
long remaining = getRemainingBase();
byte[] encryptedBlock;
if (PSVIMGConstants.FULL_PSVIMG_SIZE < remaining)
{
encryptedBlock = new byte[PSVIMGConstants.FULL_PSVIMG_SIZE];
}
else
{
encryptedBlock = new byte[remaining];
}
baseStream.Read(encryptedBlock, 0x00, encryptedBlock.Length);
byte[] decryptedBlock = aes_cbc_decrypt(encryptedBlock, iv);
return decryptedBlock;
}
}
}

View File

@ -1,212 +1,213 @@
using System;
namespace PSVIMGTOOLS
{
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;
}
internal class StringReader
{
internal static string ReadUntilTerminator(byte[] StringBytes)
{
string str = "";
foreach (byte sByte in StringBytes)
{
if (sByte != 0x00)
{
str += (char)sByte;
}
else
{
return str;
}
}
return str;
}
}
internal class SceDateTime
{
public UInt16 Year;
public UInt16 Month;
public UInt16 Day;
public UInt16 Hour;
public UInt16 Minute;
public UInt16 Second;
public UInt32 Microsecond;
public SceDateTime()
{
}
}
internal class SceIoStat
{
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 UInt32[] Private = new UInt32[6];
public SceIoStat()
{
for(int i = 0; i < Private.Length; i++)
{
Private[i] = 0;
}
}
};
internal class PsvImgTailer
{
public UInt64 Flags;
public Byte[] Padding = new Byte[1004];
public Byte[] bEnd = new Byte[12];
public String End
{
get
{
return StringReader.ReadUntilTerminator(bEnd);
}
}
}
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 class PsvImgHeader
{
public UInt64 SysTime;
public UInt64 Flags;
public SceIoStat Statistics;
public Byte[] bParentPath = new Byte[256];
public UInt32 unk_16C; // set to 1
public Byte[] bPath = new Byte[256];
public Byte[] Padding = new Byte[904];
public Byte[] bEnd = new Byte[12];
public String Path
{
get
{
return StringReader.ReadUntilTerminator(bPath);
}
}
public String End
{
get
{
return StringReader.ReadUntilTerminator(bEnd);
}
}
public String ParentPath
{
get
{
return StringReader.ReadUntilTerminator(bParentPath);
}
}
public PsvImgHeader()
{
}
}
}
using System;
namespace Vita.PsvImgTools
{
public 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;
}
internal class StringReader
{
internal static string ReadUntilTerminator(byte[] StringBytes)
{
string str = "";
foreach (byte sByte in StringBytes)
{
if (sByte != 0x00)
{
str += (char)sByte;
}
else
{
return str;
}
}
return str;
}
}
internal class SceDateTime
{
public ushort Year;
public ushort Month;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public uint Microsecond;
public SceDateTime()
{
}
}
internal class SceIoStat
{
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 ulong Size;
/** Creation time. */
public SceDateTime CreationTime;
/** Access time. */
public SceDateTime AccessTime;
/** Modification time. */
public SceDateTime ModificaionTime;
/** Device-specific data. */
public uint[] Private = new uint[6];
public SceIoStat()
{
for (int i = 0; i < Private.Length; i++)
{
Private[i] = 0;
}
}
};
internal class PsvImgTailer
{
public ulong Flags;
public byte[] Padding = new byte[1004];
public byte[] bEnd = new byte[12];
public string End
{
get
{
return StringReader.ReadUntilTerminator(bEnd);
}
}
}
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 class PsvImgHeader
{
public ulong SysTime;
public ulong Flags;
public SceIoStat Statistics;
public byte[] bParentPath = new byte[256];
public uint unk_16C; // set to 1
public byte[] bPath = new byte[256];
public byte[] Padding = new byte[904];
public byte[] bEnd = new byte[12];
public string Path
{
get
{
return StringReader.ReadUntilTerminator(bPath);
}
}
public string End
{
get
{
return StringReader.ReadUntilTerminator(bEnd);
}
}
public string ParentPath
{
get
{
return StringReader.ReadUntilTerminator(bParentPath);
}
}
public PsvImgHeader()
{
}
}
}

View File

@ -1,208 +1,208 @@
using Ionic.Zlib;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PSVIMGTOOLS
{
class PSVMDBuilder
{
private static void memset(byte[] buf, byte content, long length)
{
for (int i = 0; i < length; i++)
{
buf[i] = content;
}
}
private static void writeUInt64(Stream dst, UInt64 value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x8);
}
private static void writeInt64(Stream dst, Int64 value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x8);
}
private static void writeUInt16(Stream dst, UInt16 value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x2);
}
private static void writeInt16(Stream dst, Int16 value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x2);
}
private static void writeInt32(Stream dst, Int32 value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x4);
}
private static void writeUInt32(Stream dst, UInt32 value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x4);
}
private static void writeString(Stream dst, string str, int len = -1)
{
if (len < 0)
{
len = str.Length;
}
byte[] StrBytes = Encoding.UTF8.GetBytes(str);
dst.Write(StrBytes, 0x00, len);
}
private static void writePadding(Stream dst, byte paddingByte, long paddingLen)
{
byte[] paddingData = new byte[paddingLen];
memset(paddingData, paddingByte, paddingLen);
dst.Write(paddingData, 0x00, paddingData.Length);
}
private static void writeStringWithPadding(Stream dst, string str, int padSize, byte padByte = 0x78)
{
int StrLen = str.Length;
if (StrLen > padSize)
{
StrLen = padSize;
}
int PaddingLen = (padSize - StrLen) - 1;
writeString(dst, str, StrLen);
dst.WriteByte(0x00);
writePadding(dst, padByte, PaddingLen);
}
private static byte[] aes_ecb_decrypt(byte[] cipherText, byte[] KEY, int size = -1)
{
if (size < 0)
{
size = cipherText.Length;
}
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.ECB;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = KEY;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherText, 0, size);
cs.Close();
byte[] plainText = ms.ToArray();
return plainText;
}
private static byte[] aes_cbc_decrypt(byte[] cipherData, byte[] IV, byte[] Key)
{
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
private static byte[] aes_cbc_encrypt(byte[] plainText, byte[] IV, byte[] KEY, int size = -1)
{
if (size < 0)
{
size = plainText.Length;
}
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = KEY;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(plainText, 0, size);
cs.Close();
byte[] cipherText = ms.ToArray();
return cipherText;
}
public static void CreatePsvmd(Stream OutputStream, Stream EncryptedPsvimg, long ContentSize, string BackupType,byte[] Key)
{
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
EncryptedPsvimg.Seek(0x00, SeekOrigin.Begin);
EncryptedPsvimg.Read(IV, 0x00, IV.Length);
IV = aes_ecb_decrypt(IV, Key);
MemoryStream ms = new MemoryStream();
writeUInt32(ms, 0xFEE1900D); // magic
writeUInt32(ms, 0x2); // type
writeUInt64(ms, 0x03000000); // fw ver
ms.Write(new byte[0x10], 0x00, 0x10); // PSID
writeStringWithPadding(ms, BackupType, 0x40, 0x00); //backup type
writeInt64(ms, EncryptedPsvimg.Length); // total size
writeUInt64(ms, 0x2); //version
writeInt64(ms, ContentSize); // content size
ms.Write(IV, 0x00, 0x10); // IV
writeUInt64(ms, 0x00); //ux0 info
writeUInt64(ms, 0x00); //ur0 info
writeUInt64(ms, 0x00); //unused 98
writeUInt64(ms, 0x00); //unused A0
writeUInt32(ms, 0x1); //add data
ms.Seek(0x00, SeekOrigin.Begin);
byte[] psvMd = ms.ToArray();
ms.Close();
ms = new MemoryStream();
byte[] psvMdCompressed = ZlibStream.CompressBuffer(psvMd);
psvMdCompressed[0x1] = 0x9C;
ms.Write(psvMdCompressed, 0x00, psvMdCompressed.Length);
SHA256 sha = SHA256.Create();
byte[] shadata = sha.ComputeHash(psvMdCompressed);
ms.Write(shadata, 0x00, shadata.Length);
int PaddingLen = Convert.ToInt32(PSVIMGConstants.AES_BLOCK_SIZE - (ms.Length & (PSVIMGConstants.AES_BLOCK_SIZE - 1)));
writePadding(ms, 0x00, PaddingLen);
writeInt32(ms, PaddingLen); //Padding Length
writeUInt32(ms, 0x00);
writeInt64(ms, (ms.Length+0x8)+IV.Length);
ms.Seek(0x00, SeekOrigin.Begin);
byte[] toEncrypt = ms.ToArray();
ms.Close();
byte[] EncryptedData = aes_cbc_encrypt(toEncrypt, IV, Key);
OutputStream.Write(IV, 0x00, IV.Length);
OutputStream.Write(EncryptedData, 0x00, EncryptedData.Length);
return;
}
public static byte[] DecryptPsvmd(Stream PsvMdFile, byte[] Key)
{
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
PsvMdFile.Read(IV, 0x00, IV.Length);
byte[] remaining = new byte[PsvMdFile.Length - IV.Length];
PsvMdFile.Read(remaining, 0x00, remaining.Length);
byte[] zlibCompressed = aes_cbc_decrypt(remaining, IV, Key);
return zlibCompressed;
// return ZlibStream.UncompressBuffer(zlibCompressed);
}
}
}
using Ionic.Zlib;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Vita.PsvImgTools
{
public class PSVMDBuilder
{
private static void memset(byte[] buf, byte content, long length)
{
for (int i = 0; i < length; i++)
{
buf[i] = content;
}
}
private static void writeUInt64(Stream dst, ulong value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x8);
}
private static void writeInt64(Stream dst, long value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x8);
}
private static void writeUInt16(Stream dst, ushort value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x2);
}
private static void writeInt16(Stream dst, short value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x2);
}
private static void writeInt32(Stream dst, int value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x4);
}
private static void writeUInt32(Stream dst, uint value)
{
byte[] ValueBytes = BitConverter.GetBytes(value);
dst.Write(ValueBytes, 0x00, 0x4);
}
private static void writeString(Stream dst, string str, int len = -1)
{
if (len < 0)
{
len = str.Length;
}
byte[] StrBytes = Encoding.UTF8.GetBytes(str);
dst.Write(StrBytes, 0x00, len);
}
private static void writePadding(Stream dst, byte paddingByte, long paddingLen)
{
byte[] paddingData = new byte[paddingLen];
memset(paddingData, paddingByte, paddingLen);
dst.Write(paddingData, 0x00, paddingData.Length);
}
private static void writeStringWithPadding(Stream dst, string str, int padSize, byte padByte = 0x78)
{
int StrLen = str.Length;
if (StrLen > padSize)
{
StrLen = padSize;
}
int PaddingLen = padSize - StrLen - 1;
writeString(dst, str, StrLen);
dst.WriteByte(0x00);
writePadding(dst, padByte, PaddingLen);
}
private static byte[] aes_ecb_decrypt(byte[] cipherText, byte[] KEY, int size = -1)
{
if (size < 0)
{
size = cipherText.Length;
}
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.ECB;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = KEY;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherText, 0, size);
cs.Close();
byte[] plainText = ms.ToArray();
return plainText;
}
private static byte[] aes_cbc_decrypt(byte[] cipherData, byte[] IV, byte[] Key)
{
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
private static byte[] aes_cbc_encrypt(byte[] plainText, byte[] IV, byte[] KEY, int size = -1)
{
if (size < 0)
{
size = plainText.Length;
}
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = KEY;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(plainText, 0, size);
cs.Close();
byte[] cipherText = ms.ToArray();
return cipherText;
}
public static void CreatePsvmd(Stream OutputStream, Stream EncryptedPsvimg, long ContentSize, string BackupType, byte[] Key)
{
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
EncryptedPsvimg.Seek(0x00, SeekOrigin.Begin);
EncryptedPsvimg.Read(IV, 0x00, IV.Length);
IV = aes_ecb_decrypt(IV, Key);
MemoryStream ms = new MemoryStream();
writeUInt32(ms, 0xFEE1900D); // magic
writeUInt32(ms, 0x2); // type
writeUInt64(ms, 0x03000000); // fw ver
ms.Write(new byte[0x10], 0x00, 0x10); // PSID
writeStringWithPadding(ms, BackupType, 0x40, 0x00); //backup type
writeInt64(ms, EncryptedPsvimg.Length); // total size
writeUInt64(ms, 0x2); //version
writeInt64(ms, ContentSize); // content size
ms.Write(IV, 0x00, 0x10); // IV
writeUInt64(ms, 0x00); //ux0 info
writeUInt64(ms, 0x00); //ur0 info
writeUInt64(ms, 0x00); //unused 98
writeUInt64(ms, 0x00); //unused A0
writeUInt32(ms, 0x1); //add data
ms.Seek(0x00, SeekOrigin.Begin);
byte[] psvMd = ms.ToArray();
ms.Close();
ms = new MemoryStream();
byte[] psvMdCompressed = ZlibStream.CompressBuffer(psvMd);
psvMdCompressed[0x1] = 0x9C;
ms.Write(psvMdCompressed, 0x00, psvMdCompressed.Length);
SHA256 sha = SHA256.Create();
byte[] shadata = sha.ComputeHash(psvMdCompressed);
ms.Write(shadata, 0x00, shadata.Length);
int PaddingLen = Convert.ToInt32(PSVIMGConstants.AES_BLOCK_SIZE - (ms.Length & PSVIMGConstants.AES_BLOCK_SIZE - 1));
writePadding(ms, 0x00, PaddingLen);
writeInt32(ms, PaddingLen); //Padding Length
writeUInt32(ms, 0x00);
writeInt64(ms, ms.Length + 0x8 + IV.Length);
ms.Seek(0x00, SeekOrigin.Begin);
byte[] toEncrypt = ms.ToArray();
ms.Close();
byte[] EncryptedData = aes_cbc_encrypt(toEncrypt, IV, Key);
OutputStream.Write(IV, 0x00, IV.Length);
OutputStream.Write(EncryptedData, 0x00, EncryptedData.Length);
return;
}
public static byte[] DecryptPsvmd(Stream PsvMdFile, byte[] Key)
{
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
PsvMdFile.Read(IV, 0x00, IV.Length);
byte[] remaining = new byte[PsvMdFile.Length - IV.Length];
PsvMdFile.Read(remaining, 0x00, remaining.Length);
byte[] zlibCompressed = aes_cbc_decrypt(remaining, IV, Key);
return zlibCompressed;
// return ZlibStream.UncompressBuffer(zlibCompressed);
}
}
}

18
Vita/Vita.csproj Normal file
View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.0" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LiLib\LiLib.csproj" />
</ItemGroup>
</Project>

View File

@ -1,30 +0,0 @@
# The Bouncy Castle Crypto Package For C Sharp
The Bouncy Castle Crypto package is a C\# implementation of cryptographic algorithms and protocols, it was developed by the Legion of the Bouncy Castle, a registered Australian Charity, with a little help! The Legion, and the latest goings on with this package, can be found at [http://www.bouncycastle.org](http://www.bouncycastle.org). In addition to providing basic cryptography algorithms, the package also provides support for CMS, TSP, X.509 certificate generation and a variety of other standards such as OpenPGP.
The Legion also gratefully acknowledges the contributions made to this package by others (see [here](http://www.bouncycastle.org/csharp/contributors.html) for the current list). If you would like to contribute to our efforts please feel free to get in touch with us or visit our [donations page](https://www.bouncycastle.org/donate), sponsor some specific work, or purchase a support contract through [Crypto Workshop](http://www.cryptoworkshop.com).
Except where otherwise stated, this software is distributed under a license based on the MIT X Consortium license. To view the license, [see here](http://www.bouncycastle.org/licence.html). The OpenPGP library also includes a modified BZIP2 library which is licensed under the [Apache Software License, Version 2.0](http://www.apache.org/licenses/).
**Note**: this source tree is not the FIPS version of the APIs - if you are interested in our FIPS version please contact us directly at [office@bouncycastle.org](mailto:office@bouncycastle.org).
## Mailing Lists
For those who are interested, there are 2 mailing lists for participation in this project. To subscribe use the links below and include the word subscribe in the message body. (To unsubscribe, replace **subscribe** with **unsubscribe** in the message body)
* [announce-crypto-csharp-request@bouncycastle.org](mailto:announce-crypto-csharp-request@bouncycastle.org)
This mailing list is for new release announcements only, general subscribers cannot post to it.
* [dev-crypto-csharp-request@bouncycastle.org](mailto:dev-crypto-csharp-request@bouncycastle.org)
This mailing list is for discussion of development of the package. This includes bugs, comments, requests for enhancements, questions about use or operation.
**NOTE:**You need to be subscribed to send mail to the above mailing list.
## Feedback
If you want to provide feedback directly to the members of **The Legion** then please use [feedback-crypto@bouncycastle.org](mailto:feedback-crypto@bouncycastle.org), if you want to help this project survive please consider [donating](https://www.bouncycastle.org/donate).
For bug reporting/requests you can report issues here on github, via feedback-crypto if required, and we also have a [Jira issue tracker](http://www.bouncycastle.org/jira). We will accept pull requests based on this repository as well.
## Finally
Enjoy!

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff