Editing Kirk
Jump to navigation
Jump to search
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 46: | Line 46: | ||
</pre> | </pre> | ||
These commands allow to do operations with any public key. For the latest | These commands allow to do operations with any public key. For the latest Pre-IPL version which adds an additional ECDSA verification of the XOR of the block hashes, the public key which is hardcoded in the Pre-IPL is (0xBC660611A70BD7F2D140A48215C096D11D2D4112, 0xF0E9379AC4E0D387C542D091349DD15169DD5A87). | ||
== Code sample == | == Code sample == | ||
Line 146: | Line 146: | ||
|} | |} | ||
= | = Per-console keys = | ||
Kirk commands | Some Kirk commands like commands 16 and 18 use individual (per-console) seeds. The base per-console seed is the Fuse ID (6 bytes), which is transformed into a 0x30 bytes buffer ("key mesh"). This buffer is used to generate different keys depending on a seed. | ||
= | {| class="wikitable" | ||
|+ | |||
!Seed | |||
!Usage | |||
|- | |||
|0 | |||
|Kirk commands 2 (encryption) & 3 (decryption) (the real encryption & CMAC keys are random, but this per-console key is used to encrypt them) | |||
|- | |||
|1 | |||
|Kirk command 5 (encryption) & 8 (decryption) | |||
|- | |||
|2 | |||
|Kirk command 6 (encryption) & 9 (decryption) | |||
|- | |||
|3 | |||
|Kirk command 16 | |||
|- | |||
|4 | |||
|Kirk command 18 | |||
|- | |||
|5 | |||
|Unused | |||
|- | |||
|6 | |||
|RNG buffer reseeding | |||
|} | |||
<source lang="c"> | <source lang="c"> | ||
typedef struct | typedef struct ScePspKeyMesh { // size is 0x30 | ||
SceUInt8 | SceUInt8 aes128cbc_key_1[0x10]; // used by Kirk commands 5 & 8 and 16 | ||
SceUInt8 | SceUInt8 aes128cbc_key_2[0x10]; // used by Kirk command 2 & 3, 6 & 9 and 18 | ||
SceUInt8 derivation_key[0x10]; // | SceUInt8 derivation_key[0x10]; // used to derive the 2 other keys | ||
} | } ScePspKeyMesh; | ||
</source> | </source> | ||
To generate the key mesh of a PSP, provided the Fuse ID (0xBC100090 and 0xBC100094 hardware registers), execute the following code. | |||
To generate the | |||
<source lang="c"> | <source lang="c"> | ||
void | void gen_psp_individual_seed() { | ||
int i, k; | int i, k; | ||
ScePspKeyMesh seed; | |||
u8 subkey_1[0x10], subkey_2[0x10]; | u8 subkey_1[0x10], subkey_2[0x10]; | ||
rijndael_ctx aes_ctx; | rijndael_ctx aes_ctx; | ||
u8 | u8 fuseid[8]; | ||
// Byte-reverse the Fuse ID | // Byte-reverse the Fuse ID | ||
u32 g_fuse90 = *(u32 *)0xBC100090; | u32 g_fuse90 = *(u32 *)0xBC100090; | ||
u32 g_fuse94 = *(u32 *)0xBC100094; | u32 g_fuse94 = *(u32 *)0xBC100094; | ||
fuseid[7] = g_fuse90 &0xFF; | |||
fuseid[6] = (g_fuse90>>8) &0xFF; | |||
fuseid[5] = (g_fuse90>>16) &0xFF; | |||
fuseid[4] = (g_fuse90>>24) &0xFF; | |||
fuseid[3] = g_fuse94 &0xFF; | |||
fuseid[2] = (g_fuse94>>8) &0xFF; | |||
fuseid[1] = (g_fuse94>>16) &0xFF; | |||
fuseid[0] = (g_fuse94>>24) &0xFF; | |||
rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key | rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key | ||
for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID | for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID | ||
subkey_2[i] = subkey_1[i] = | subkey_2[i] = subkey_1[i] = fuseid[i % 8]; | ||
for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times | for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times | ||
Line 197: | Line 219: | ||
rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key | rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key | ||
for (i = 0; i < 3; i++) { // encrypt 3, 6 and 9 times subkey_2 to obtain the final | for (i = 0; i < 3; i++) { // encrypt 3, 6 and 9 times the subkey_2 to obtain the final keymesh | ||
for (k = 0; k < 3; k++) | for (k = 0; k < 3; k++) | ||
rijndael_encrypt(&aes_ctx, subkey_2, subkey_2); | rijndael_encrypt(&aes_ctx, subkey_2, subkey_2); | ||
memcpy( | memcpy(&seed[i * 0x10], subkey_2, 0x10); | ||
} | } | ||
} | } | ||
</source>The key mesh can then be used along with a seed to generate a key using the following algorithm:<syntaxhighlight lang="c"> | |||
void make_perconsole_key(u8 output[16], int seed, ScePspKeyMesh keymesh) | |||
{ | |||
if (seed & 1) { | |||
memcpy(output, keymesh.aes128cbc_key_2, 16); | |||
} else { | |||
memcpy(output, keymesh.aes128cbc_key_1, 16); | |||
} | |||
// Encrypt the result several times depending on the seed | |||
rijndael_set_key(&aes_ctx, keymesh.aes128cbc_derivation_key); | |||
seed = (seed / 2) + 1; | |||
while ((seed--) >= 0) { | |||
</source> | |||
<syntaxhighlight lang="c"> | |||
void make_perconsole_key(u8 output[16], int | |||
if ( | |||
memcpy(output, | |||
else | |||
memcpy(output, | |||
// Encrypt the result several times depending on the seed | |||
rijndael_set_key(&aes_ctx, | |||
while (( | |||
rijndael_encrypt(&aes_ctx, output); | rijndael_encrypt(&aes_ctx, output); | ||
} | } | ||
Line 285: | Line 242: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == 0x40-byte buffer == | ||
There | There is a 0x40-byte buffer, named here <code>ScePspIndividualSeed</code>, used in both PSP flashData.prx and (to be checked) in PS Vita cmep keyrings 0x601 and 0x602. It might be slightly different from the mesh buffer described above, so we keep it for now. Indeed, it is unsure if this 0x40 bytes buffer on PS Vita holds the final AES keys or if it is before applying the derivation_key. | ||
Some Kirk commands like commands 16 and 18 use individual (per-console) seeds. The base per-console seed is the Fuse ID (6 bytes), which is transformed into a 0x40 bytes buffer. The first 0x10 bytes of the buffer is the AES CBC MAC key used by Kirk command 18 whilst the second 0x10 bytes are the AES CBC key used by Kirk command 16. | |||
<source lang="C"> | <source lang="C"> | ||
typedef struct | typedef struct ScePspIndividualSeed { // size is 0x40 | ||
SceUInt8 aes128cbc_mac_key[0x10]; // used by Kirk command 18 | |||
SceUInt8 aes128cbc_key[0x10]; // used by Kirk command 16 | |||
SceUInt8 derivation_key[0x10]; // used to derive the 2 other keys of the structure | |||
SceUInt8 fuse_id[8]; // endianness to precise | SceUInt8 fuse_id[8]; // endianness to precise | ||
SceUInt32 padding; // usually set to zero | |||
SceUInt32 hash; // the hash algorithm is in PSP Jig Kick flashData.prx | SceUInt32 hash; // the hash algorithm is in PSP Jig Kick flashData.prx | ||
} | } ScePspIndividualSeed; | ||
</source> | </source> | ||
=== | To generate (without the hash though, and untested) ScePspIndividualSeed, provided the Fuse ID (0xBC100090 and 0xBC100094 hardware registers), execute the following code. | ||
<source lang="C"> | |||
void gen_psp_individual_seed() { | |||
int i, k; | |||
ScePspIndividualSeed seed; | |||
u8 subkey_1[0x10], subkey_2[0x10]; | |||
rijndael_ctx aes_ctx; | |||
u8 fuseid[8]; | |||
// Byte-reverse the Fuse ID | |||
u32 g_fuse90 = *(u32 *)0xBC100090; | |||
u32 g_fuse94 = *(u32 *)0xBC100094; | |||
fuseid[7] = g_fuse90 &0xFF; | |||
fuseid[6] = (g_fuse90>>8) &0xFF; | |||
fuseid[5] = (g_fuse90>>16) &0xFF; | |||
fuseid[4] = (g_fuse90>>24) &0xFF; | |||
fuseid[3] = g_fuse94 &0xFF; | |||
fuseid[2] = (g_fuse94>>8) &0xFF; | |||
fuseid[1] = (g_fuse94>>16) &0xFF; | |||
fuseid[0] = (g_fuse94>>24) &0xFF; | |||
rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key | |||
for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID | |||
subkey_2[i] = subkey_1[i] = fuseid[i % 8]; | |||
for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times | |||
rijndael_encrypt(&aes_ctx, subkey_1, subkey_1); | |||
rijndael_decrypt(&aes_ctx, subkey_2, subkey_2); | |||
} | |||
rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key | |||
for (i = 0; i < 3; i++) { // encrypt three times each one of the three first blocks | |||
for (k = 0; k < 3; k++) | |||
rijndael_encrypt(&aes_ctx, subkey_2, subkey_2); | |||
memcpy(&seed[i * 0x10], subkey_2, 0x10); | |||
} | |||
rijndael_set_key(&aes_ctx, seed.derivation_key, 128); // set the derivation key as AES key | |||
for (i = 0; i < 2; i++) { // encrypt twice the seeds to get the final keys | |||
rijndael_encrypt(&aes_ctx, seed.aes128cbc_mac_key, seed.aes128cbc_mac_key); | |||
rijndael_encrypt(&aes_ctx, seed.aes128cbc_key, seed.aes128cbc_key); | |||
} | |||
} | |||
</source> | |||
The following code is the official way to build the 0x40-byte buffer: | |||
<source lang="C"> | <source lang="C"> | ||
void | void gen_hash_at_0x3C(byte *buf) { | ||
byte bVar1; | byte bVar1; | ||
uint uVar2; | uint uVar2; | ||
Line 344: | Line 320: | ||
uint uVar8; | uint uVar8; | ||
byte bVar9; | byte bVar9; | ||
int | int i; | ||
int | int j; | ||
byte *pbVar11; | byte *pbVar11; | ||
byte local_60 [80]; | byte local_60 [80]; | ||
Line 357: | Line 333: | ||
m[3] = 0x78; | m[3] = 0x78; | ||
m[4] = 0x40; | m[4] = 0x40; | ||
j = 0; | |||
do { | do { | ||
pbVar4 = | pbVar4 = buf + j; | ||
pbVar7 = local_60 + | pbVar7 = local_60 + j; | ||
j = j + 1; | |||
*pbVar7 = *pbVar4; | *pbVar7 = *pbVar4; | ||
} while ( | } while (j < 0x3c); | ||
j = 0x3c; | |||
do { | do { | ||
pbVar7 = local_60 + | pbVar7 = local_60 + j; | ||
j = j + 1; | |||
*pbVar7 = 0; | *pbVar7 = 0; | ||
} while ( | } while (j < 0x40); | ||
j = 0; | |||
do { | do { | ||
bVar1 = *pbVar11; | bVar1 = *pbVar11; | ||
i = 0; | |||
do { | do { | ||
uVar8 = (uint)m[ | uVar8 = (uint)m[i]; | ||
iVar3 = | iVar3 = i + 0x40; | ||
uVar10 = 0; | uVar10 = 0; | ||
bVar9 = 0; | bVar9 = 0; | ||
Line 393: | Line 365: | ||
bVar9 = (byte)uVar10; | bVar9 = (byte)uVar10; | ||
uVar2 = uVar6; | uVar2 = uVar6; | ||
if ((uVar6 & 0x100) != 0) | if ((uVar6 & 0x100) != 0) { | ||
uVar2 = uVar6 ^ 0x11d; | uVar2 = uVar6 ^ 0x11d; | ||
} | |||
} | } | ||
i = i + 1; | |||
local_60[iVar3] = bVar9; | local_60[iVar3] = bVar9; | ||
} while ( | } while (i < 5); | ||
i = 0; | |||
do { | do { | ||
pbVar7 = pbVar11 + | pbVar7 = pbVar11 + i; | ||
iVar3 = | iVar3 = i + 0x40; | ||
i = i + 1; | |||
*pbVar7 = *pbVar7 ^ local_60[iVar3]; | *pbVar7 = *pbVar7 ^ local_60[iVar3]; | ||
} while ( | } while (i < 5); | ||
i = j + 1; | |||
pbVar11 = local_60 + j + 1; | |||
pbVar11 = local_60 + | j = i; | ||
} while (i < 0x3c); | |||
} while ( | j = 0x3c; | ||
do { | do { | ||
pbVar11 = local_60 + | pbVar11 = local_60 + j; | ||
pbVar7 = | pbVar7 = buf + j; | ||
j = j + 1; | |||
*pbVar7 = *pbVar11; | *pbVar7 = *pbVar11; | ||
*pbVar11 = 0; | *pbVar11 = 0; | ||
} while ( | } while (j < 0x40); | ||
return; | return; | ||
} | } | ||
uint generate_ids_seed_value(byte *ss,byte *data_for_0x38,byte *fuse_keys) { | |||
byte bVar1; | |||
byte *dst; | |||
int idx; | |||
int j; | |||
byte *src; | |||
byte buf2 [16]; | |||
byte buf1 [16]; | |||
uint ctx [64]; | |||
uint ctx2 [64]; | |||
AES_set_encrypt_key_2(g_ids_master_key,0x80,ctx); | |||
AES_set_decrypt_key_2(g_ids_master_key,0x80,ctx2); | |||
idx = 0; | |||
do { | |||
bVar1 = ss[idx + ((int)(idx + ((uint)(idx >> 0x1f) >> 0x1d)) >> 3) * -8]; | |||
src = buf2 + idx; | |||
dst = buf1 + idx; | |||
idx = idx + 1; | |||
*src = bVar1; | |||
*dst = bVar1; | |||
} while (idx < 0x10); | |||
idx = 2; | |||
do { | |||
AES_encrypt_2(buf1,buf1,ctx); | |||
idx = idx + -1; | |||
AES_decrypt_2(buf2,buf2,ctx2); | |||
} while (-1 < idx); | |||
AES_set_encrypt_key_2(buf1,0x80,ctx); | |||
idx = 0; | |||
do { | |||
j = 2; | |||
do { | |||
j = j + -1; | |||
AES_encrypt_2(buf2,buf2,ctx); | |||
} while (-1 < j); | |||
dst = fuse_keys + idx * 0x10; | |||
} | j = 0; | ||
do { | |||
src = buf2 + j; | |||
j = j + 1; | |||
*dst = *src; | |||
dst = dst + 1; | |||
} while (j < 0x10); | |||
idx = idx + 1; | |||
} while (idx < 3); | |||
idx = 0; | |||
do { | |||
j = idx + 1; | |||
fuse_keys[idx + 0x30] = ss[idx]; | |||
idx = j; | |||
} while (j < 8); | |||
idx = 0; | |||
do { | |||
j = idx + 1; | |||
fuse_keys[idx + 0x38] = data_for_0x38[idx]; | |||
idx = j; | |||
} while (j < 4); | |||
gen_hash_at_0x3C(fuse_keys); | |||
return 0; | return 0; | ||
} | } | ||
</source> | </source> | ||
= Commands = | = Commands = | ||
Line 970: | Line 937: | ||
== Command 0x11: ECDSA signature verification == | == Command 0x11: ECDSA signature verification == | ||
This command verifies an ECDSA signature | This command verifies an ECDSA signature. | ||
It takes no output, and takes as an input: | It takes no output, and takes as an input: | ||
Line 981: | Line 948: | ||
== Command 0x12: verify certificate == | == Command 0x12: verify certificate == | ||
This command has no output. | This command has no output. | ||
It takes as input | It takes as an input a 0xB8-long buffer: | ||
*0x00: certificate data (either ConsoleID or OpenPSID) and ECDSA signature etc. (unused here) | |||
*0xA8: AES-CMAC hash of the rest of the header. | |||
It verifies the AES CMAC of the header using per-console key with seed 4. | |||
It | |||
= Error codes = | = Error codes = |