Editing Kirk

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

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 20: Line 20:
Both use the usual Weierstrass form.
Both use the usual Weierstrass form.


== Elliptic curve for Kirk commands 1/2/3/0xA ==
== Elliptic curve for Kirk commands 1/2/3/10 ==


This curve is used for the ECDSA verification of Kirk commands 1, 2, 3 and 0xA.
This curve is used for the ECDSA verification of Kirk commands 1, 2, 3 and 10.


<pre>
<pre>
Line 36: Line 36:
== Elliptic curve for the other commands ==
== Elliptic curve for the other commands ==


This curved is used for Kirk commands 0xC, 0xD, 0xE, 0x10 and 0x11.
This curved is used for Kirk commands 12, 13, 14, 16 and 17.


<pre>
<pre>
Line 46: Line 46:
</pre>
</pre>


These commands allow to do operations with any public key. For the latest [[iplloader]] version which adds an additional ECDSA verification of the XOR of the block hashes, the public key which is hardcoded in the iplloader is (0xBC660611A70BD7F2D140A48215C096D11D2D4112, 0xF0E9379AC4E0D387C542D091349DD15169DD5A87).
The public key is variable. For the latest Pre-IPL version which add an additional ECDSA verification of the XOR of the block hashes, the public key is (0xBC660611A70BD7F2D140A48215C096D11D2D4112, 0xF0E9379AC4E0D387C542D091349DD15169DD5A87).


== Code sample ==
== Code sample ==
Line 116: Line 116:
|No
|No
|-
|-
|4~0x83
|4~131
|KIRK commands 4/7 decryption keys (128 possible ones)
|KIRK commands 4/7 decryption keys (128 possible ones)
|Yes
|Yes
Line 146: Line 146:
|}
|}


= PSP Individual Keys =
= Per-console keys =
 
Kirk commands 2, 3, 5, 6, 8, 9, 0x10 and 0x12 use individual (per-console) seeds to generate individual keys. The base per-console seed is the Fuse ID (6 bytes), which is transformed into a 0x30-byte buffer named unofficially "individual key mesh". The PSP individual key mesh is used to generate various final individual keys depending on a seed parameter.
 
== PSP Individual Key Mesh ==
 
=== Structure ===
 
<source lang="c">
typedef struct ScePspIndividualKeyMesh { // size is 0x30
    SceUInt8 derivation_seed_0[0x10]; // a seed used to derive final keys with derivation_key
    SceUInt8 derivation_seed_1[0x10]; // a seed used to derive final keys with derivation_key
    SceUInt8 derivation_key[0x10]; // AES128 key used to derive final keys from seed_0 and seed_1
} ScePspIndividualKeyMesh;
</source>
 
=== Algorithm ===
 
To generate the individual key mesh of a specific PSP, provided its [[Fuse ID]], execute the following code.
 
<source lang="c">
void gen_psp_individual_key_mesh(ScePspIndividualKeyMesh *key_mesh) {
  int i, k;
  u8 subkey_1[0x10], subkey_2[0x10];
  rijndael_ctx aes_ctx;
  u8 fuse_id[8];
 
  // Byte-reverse the Fuse ID
  u32 g_fuse90 = *(u32 *)0xBC100090;
  u32 g_fuse94 = *(u32 *)0xBC100094;
  fuse_id[7] = g_fuse90 &0xFF;
  fuse_id[6] = (g_fuse90>>8) &0xFF;
  fuse_id[5] = (g_fuse90>>16) &0xFF;
  fuse_id[4] = (g_fuse90>>24) &0xFF;
  fuse_id[3] = g_fuse94 &0xFF;
  fuse_id[2] = (g_fuse94>>8) &0xFF;
  fuse_id[1] = (g_fuse94>>16) &0xFF;
  fuse_id[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] = fuse_id[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 3, 6 and 9 times subkey_2 to obtain the final key mesh
    for (k = 0; k < 3; k++)
      rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
    memcpy(key_mesh[i * 0x10], subkey_2, 0x10);
  }
}
 
typedef struct {
unsigned char buf1[8]; // 0
unsigned char buf2[8]; // 8
unsigned char buf3[8]; // 0x10
} SomeStructure;
 
void gen_psp_individual_key_mesh_official_implementation(SomeStructure *ss, ScePspIndividualKeyMesh *key_mesh) {
  byte bVar1;
  byte *dst;
  int idx;
  int j;
  byte *src;
  byte subkey_2[16];
  byte subkey_1[16];
  uint ctx[64];
  uint ctx2[64];
 
  AES_set_encrypt_key_2(g_ids_master_key, 128, ctx); // set g_ids_master_key as AES key
  AES_set_decrypt_key_2(g_ids_master_key, 128, ctx2); // set g_ids_master_key as AES key
 
  idx = 0; // initialize the subkeys using the Fuse ID
  do {
    bVar1 = ss[idx + ((int)(idx + ((uint)(idx >> 0x1f) >> 0x1d)) >> 3) * -8];
    src = subkey_2 + idx;
    dst = subkey_1 + idx;
    idx = idx + 1;
    *src = bVar1;
    *dst = bVar1;
  } while (idx < 0x10);
 
  idx = 2; // encrypt first subkey three times, and decrypt second subkey three times
  do {
    AES_encrypt_2(subkey_1, subkey_1, ctx);
    idx = idx - 1;
    AES_decrypt_2(subkey_2, subkey_2, ctx2);
  } while (-1 < idx);
 
  AES_set_encrypt_key_2(subkey_1, 128, ctx); // set subkey_1 as AES key
 
  idx = 0; // encrypt three times each one of the three first blocks
  do {
    j = 2;
    do {
      j = j - 1;
      AES_encrypt_2(subkey_2, subkey_2, ctx);
    } while (-1 < j);
    dst = key_mesh + idx * 0x10;
    j = 0;
    do {
      src = subkey_2 + j;
      j = j + 1;
      *dst = *src;
      dst = dst + 1;
    } while (j < 0x10);
    idx = idx + 1;
  } while (idx < 3);
}
</source>
 
== Final PSP Individual Keys ==
 
=== Algorithm ===
 
In some Kirk commands, the individual key mesh is used along with a seed parameter to generate a final individual key using the following algorithm.
 
<syntaxhighlight lang="c">
void make_perconsole_key(u8 output[16], int seed_param, ScePspIndividualKeyMesh *key_mesh) {
    if (seed_param & 1)
        memcpy(output, key_mesh->derivation_seed_1, 16);
    else
        memcpy(output, key_mesh->derivation_seed_0, 16);
 
    // Encrypt the result several times depending on the seed parameter
    rijndael_set_key(&aes_ctx, key_mesh->derivation_key);
    seed_param = (seed_param / 2) + 1;
    while ((seed_param--) >= 0) {
        rijndael_encrypt(&aes_ctx, output);
    }
}
</syntaxhighlight>
 
=== Seed Parameter Per Command ===


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"
{| class="wikitable"
|+
|+
!Seed parameter
!Seed
!Usage
!Usage
|-
|-
|0
|0
|Kirk commands 2 (encryption) & 3 (decryption) (the real encryption and CMAC keys are random, but this per-console key is used to encrypt them)
|Kirk commands 2 (encryption) & 3 (decryption) (the real encryption & CMAC keys are random, but this per-console key is used to encrypt them)
|-
|-
|1
|1
Line 313: Line 175:
|RNG buffer reseeding
|RNG buffer reseeding
|}
|}
 
<source lang="c">
== PSP Individual Key Mesh Certificate ==
typedef struct ScePspKeyMesh { // size is 0x30
 
     SceUInt8 aes128cbc_key_1[0x10]; // used by Kirk commands 5 & 8 and 16
There exists a PSP Individual Key Mesh Certificate stored in both PSP flashData.prx and in PS Vita cmep keyrings 0x601 and 0x602 (in endian-swapped fashion). It contains the individual key mesh followed by the Fuse ID from which it was generated and ends with a hash.
     SceUInt8 aes128cbc_key_2[0x10]; // used by Kirk command 2 & 3, 6 & 9 and 18
 
     SceUInt8 derivation_key[0x10]; // used to derive the 2 other keys
=== Structure ===
} ScePspKeyMesh;
 
<source lang="C">
typedef struct ScePspIndividualKeyMeshCert { // size is 0x40
    ScePspIndividualKeyMesh key_mesh;
     SceUInt8 fuse_id[8]; // endianness to precise
     SceUInt8 reserved[4]; // could be arbitrary but in practice always zeroed
     SceUInt32 hash; // the hash algorithm is in PSP Jig Kick flashData.prx
} ScePspIndividualKeyMeshCert;
</source>
</source>


=== Algorithm ===
To generate the key mesh of a PSP, provided the Fuse ID (0xBC100090 and 0xBC100094 hardware registers), execute the following code.


To generate the ScePspIndividualKeyMeshCert of a specific PSP, provided its [[Fuse ID]], execute the following code.
<source lang="c">
 
void gen_psp_individual_seed() {  
<source lang="C">
   int i, k;
void gen_psp_individual_key_mesh_certificate_hash(ScePspIndividualKeyMeshCert *cert) {
   ScePspKeyMesh seed;
   byte bVar1;
   u8 subkey_1[0x10], subkey_2[0x10];
   uint uVar2;
   rijndael_ctx aes_ctx;
   int iVar3;
   u8 fuseid[8];
   byte *pbVar4;
    
   uint uVar5;
  // Byte-reverse the Fuse ID
   uint uVar6;
  u32 g_fuse90 = *(u32 *)0xBC100090;
   byte *pbVar7;
   u32 g_fuse94 = *(u32 *)0xBC100094;
   uint uVar8;
   fuseid[7] = g_fuse90 &0xFF;
   byte bVar9;
   fuseid[6] = (g_fuse90>>8) &0xFF;
   int idx;
   fuseid[5] = (g_fuse90>>16) &0xFF;
   int offset;
   fuseid[4] = (g_fuse90>>24) &0xFF;
   byte *pbVar11;
   fuseid[3] = g_fuse94 &0xFF;
   byte local_60 [80];
   fuseid[2] = (g_fuse94>>8) &0xFF;
   byte m [16];
   fuseid[1] = (g_fuse94>>16) &0xFF;
   uint uVar10;
  fuseid[0] = (g_fuse94>>24) &0xFF;
   rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key
    
    
   pbVar11 = local_60;
   for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID
  m[0] = 1;
    subkey_2[i] = subkey_1[i] = fuseid[i % 8];
  m[1] = 0xf;
  m[2] = 0x36;
  m[3] = 0x78;
  m[4] = 0x40;


   offset = 0;
   for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times
  do {
     rijndael_encrypt(&aes_ctx, subkey_1, subkey_1);
    pbVar4 = cert + offset;
     rijndael_decrypt(&aes_ctx, subkey_2, subkey_2);
    pbVar7 = local_60 + offset;
   }
     offset = offset + 1;
     *pbVar7 = *pbVar4;
   } while (offset < 0x3c);


   offset = 0x3c;
   rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key
  do {
    pbVar7 = local_60 + offset;
    offset = offset + 1;
    *pbVar7 = 0;
  } while (offset < 0x40);


   offset = 0;
   for (i = 0; i < 3; i++) { // encrypt 3, 6 and 9 times the subkey_2 to obtain the final keymesh
  do {
    for (k = 0; k < 3; k++)
    bVar1 = *pbVar11;
       rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
 
     memcpy(&seed[i * 0x10], subkey_2, 0x10);
    idx = 0;
   }
    do {
      uVar8 = (uint)m[idx];
      iVar3 = idx + 0x40;
      uVar10 = 0;
      bVar9 = 0;
      uVar2 = (uint)bVar1;
      while (uVar8 != 0) {
        uVar6 = uVar2 << 1;
        uVar5 = uVar8 & 1;
        uVar8 = (int)uVar8 >> 1;
        if (uVar5 != 0) {
          uVar10 = uVar10 ^ uVar2;
        }
        bVar9 = (byte)uVar10;
        uVar2 = uVar6;
        if ((uVar6 & 0x100) != 0)
          uVar2 = uVar6 ^ 0x11d;
      }
      idx = idx + 1;
      local_60[iVar3] = bVar9;
    } while (idx < 5);
 
    idx = 0;
    do {
      pbVar7 = pbVar11 + idx;
      iVar3 = idx + 0x40;
      idx = idx + 1;
       *pbVar7 = *pbVar7 ^ local_60[iVar3];
    } while (idx < 5);
 
     idx = offset + 1;
    pbVar11 = local_60 + offset + 1;
    offset = idx;
  } while (idx < 0x3c);
 
  offset = 0x3c;
  do {
    pbVar11 = local_60 + offset;
    pbVar7 = cert + offset;
    offset = offset + 1;
    *pbVar7 = *pbVar11;
    *pbVar11 = 0;
   } while (offset < 0x40);
 
  return;
}
}
 
</source>The key mesh can then be used along with a seed to generate a key using the following algorithm:<syntaxhighlight lang="c">
void gen_psp_individual_key_mesh_certificate(SomeStructure *ss, byte *data_for_0x38, ScePspIndividualKeyMeshCert *cert) {  
void make_perconsole_key(u8 output[16], int seed, ScePspKeyMesh keymesh)
  gen_psp_key_mesh(cert->key_mesh);
{
 
    if (seed & 1) {
  for (int idx = 0; idx < 8; idx++)
        memcpy(output, keymesh.aes128cbc_key_2, 16);
     cert->fuse_id[idx] = ss[idx];
    } else {
 
        memcpy(output, keymesh.aes128cbc_key_1, 16);
  for (int idx = 0; idx < 4; idx++)
     }
     cert->reserved[idx] = data_for_0x38[idx];
    // Encrypt the result several times depending on the seed
 
    rijndael_set_key(&aes_ctx, keymesh.aes128cbc_derivation_key);
  gen_psp_individual_key_mesh_certificate_hash(cert);
    seed = (seed / 2) + 1;
 
     while ((seed--) >= 0) {
  return 0;
        rijndael_encrypt(&aes_ctx, output);
    }
}
}
 
</syntaxhighlight>
typedef struct U64 {
unsigned int low;
unsigned int high;
} U64;
 
int CreateSomeStructure(SomeStructure *ss) {
U64 fuse_id;
int i;
 
memcpy(&fuse_id, &g_fuse_id, 8);
 
memset(ss->buf1, 0, 8);
memset(ss->buf2, 0xFF, 8);
 
memcpy(ss->buf3, &fuse_id.high, 4);
memcpy(ss->buf3+4, &fuse_id.low, 4);
 
for (i = 0; i < 4; i++) {
ss->buf1[3-i] = ss->buf3[i];
ss->buf1[7-i] = ss->buf3[4+i];
}
 
return 0;
}
 
uint gen_psp_individual_seed_helper(ScePspIndividualKeyMeshCert *cert) {
  SomeStructure ss;
  CreateSomeStructure(&ss);
  int data_for_0x38 = 0;
  gen_psp_individual_key_mesh_certificate(&ss, &data_for_0x38, cert)
  return 0;
}
</source>
 
= Mapping Structure =
<pre>
0xBDE00000 = Kirk Signature
0xBDE00004 = Kirk Version
0xBDE00008 = Kirk Error
0xBDE0000C = Kirk Proc Phase
0xBDE00010 = Kirk CMD Number
0xBDE00014 = Kirk Result
0xBDE00018 = Unknown?
0xBDE0001C = Kirk Status
0xBDE00020 = Kirk Status Asynchronous
0xBDE00024 = Kirk Status Asynchronous End
0xBDE00028 = Kirk Status End
0xBDE0002C = Kirk Source Address
0xBDE00030 = Kirk Destination Address
</pre>


= Commands =
= Commands =
Line 601: Line 351:
| 10 (0xA)
| 10 (0xA)
| KIRK_CMD_PRIV_SIGVRY
| KIRK_CMD_PRIV_SIGVRY
| Private Signature Verify (checks for private SCE signature)
| Private Signature Verify (checks for private SCE sig)
| buf_size+0x90
| buf_size+0x90
| 0
| 0
Line 645: Line 395:
|-
|-
| 15 (0xF)
| 15 (0xF)
| KIRK_CMD_SEED
| KIRK_CMD_INIT_FUSE_SEEDS
| Seed the Kirk internal RNG buffer
| Initializes Kirk Fuse Seeds.
| 0x1C
| 0x1C
| 0x1C
| 0x1C
Line 658: Line 408:
| 0x34
| 0x34
| 0x28
| 0x28
| memab, openpsid (used for IDStorage Certificates ECDSA)
| memab, openpsid
| {{yes}}
| {{yes}}
| {{no}}
| {{no}}
Line 664: Line 414:
| 17 (0x11)
| 17 (0x11)
| KIRK_CMD_SIGVRY
| KIRK_CMD_SIGVRY
| ECDSA Signature Verification
| Signature Verification (checks for generated signatures)
| 0x64
| 0x64
| 0
| 0
| memab, memlmd, mesg_led, openpsid (checks for generated signatures, used for IDStorage Certificates ECDSA)
| memab, memlmd, mesg_led, openpsid
| {{no}}
| {{no}}
| {{no}}
| {{no}}
Line 673: Line 423:
| 18 (0x12)
| 18 (0x12)
| KIRK_CMD_CERTVRY
| KIRK_CMD_CERTVRY
| Certificate Verification
| Certificate Verification (IDStorage Certificates CMAC)
| 0xB8
| 0xB8
| 0
| 0
| openpsid, memab, chkreg (used for IDStorage Certificates AES-CMAC)
| openpsid, memab, chkreg
| {{yes}}
| {{yes}}
| {{no}}
| {{no}}
|}
|}


== Command 0x0: decrypt kbooti ==
== Command 0: decrypt kbooti ==
This command is only used by devkits to decrypt the kbooti, ie the devkit's Bootrom. It supposedly can only be run at a very early stage. The very short header is as follows.
This command is only used by devkits to decrypt the kbooti, ie the devkit's Pre-IPL. It supposedly can only be run at a very early stage. The very short header is as follows.
{| class="wikitable"
{| class="wikitable"
|+
|+
Line 709: Line 459:
# Decrypt body using AES slotted key 0
# Decrypt body using AES slotted key 0


== Commands 0x1, 0x2, 0x3 & 0xA: decryption and authentication ==
== Commands 1, 2, 3 & 10: decryption and authentication ==


=== Overview ===
=== Overview ===
Line 718: Line 468:
* Command 2 is used to decrypt DRMBB and reencrypt them using a (random key encrypted with a) per-console key to generate data to pass to command 3.
* Command 2 is used to decrypt DRMBB and reencrypt them using a (random key encrypted with a) per-console key to generate data to pass to command 3.
* Command 3 decrypts data encrypted by command 2.
* Command 3 decrypts data encrypted by command 2.
* Command 0xA takes the same data as commands 1, 2 and 3 but only does the signature verification for the header (not for the body) and no decryption (or reencryption).
* Command 10 takes the same data as commands 1, 2 and 3 but only does the signature verification for the header (not for the body) and no decryption (or reencryption). It is used to verify IdStorage IDPS certificates.


There are two versions of this service: AES CMAC verification, and ECDSA verification. They use the header section of the input buffer slightly differently.
There are two versions of this service: AES CMAC verification, and ECDSA verification. They use the header section of the input buffer slightly differently.
Line 822: Line 572:
# Generate a valid CMAC or ECDSA signature for the output. For ECDSA, this uses the private key stored in key slot 4 (and is the private counterpart of slots 5/6 used by command 3).
# Generate a valid CMAC or ECDSA signature for the output. For ECDSA, this uses the private key stored in key slot 4 (and is the private counterpart of slots 5/6 used by command 3).


=== Command 0xA ===
=== Command 10 ===
Its behavior is very simple:
Its behavior is very simple:


Line 831: Line 581:
== Commands 4~9: AES encrypt & decrypt ==
== Commands 4~9: AES encrypt & decrypt ==


All these commands do AES128-CBC encryption/decryption with an IV equal to 0. The encryption operands take a header as an input along with the raw data, and generate encrypted data along with a header corresponding to the matching decryption command. Decryption commands output the raw decrypted data.
All these commands do AES128-CBC encryption/decryption with an IV equal to 0.
- Commands 4 (encryption) and 7 (decryption) use a one of the 128 keys stored in the Kirk chip and available on the [[Keys]] page, index being given by the keyseed field (which must be between 0x00 and 0x7F)
- Commands 5 (encryption) and 8 (decryption) use an unknown per-console key (it is unknown if it is derived from other data, or just stored as-is on the chip)
- Commands 6 (encryption) and 9 (decryption) use a key derived from the keyseed using an unknown key derivation function


- Commands 4 (encryption) and 7 (decryption) use a one of the 128 keys stored in the Kirk chip and available on the [[Keys]] page, index being given by the keyseed field (which must be between 0x00 and 0x7F), with console-specific modifications for some keyseeds
In all cases, data is prefixed with a 0x14-byte long header:
 
- Commands 5 (encryption) and 8 (decryption) use a per-console key derived from the key mesh
 
- Commands 6 (encryption) and 9 (decryption) use a key derived from a random key and data stored at 0x14, the random key being encrypted with a per-console key so that command 9 can decrypt
 
In all cases, data is prefixed with a 0x14-byte long header (except for commands 6 and 9 where it is longer).
{| class="wikitable"
{| class="wikitable"
|-
|-
! Address !! Size !! Description
! Address !! Size !! Description
|-
|-
| 0x00 || 4 || Mode: must be 4 for encryption (commands 4/5/6), 5 for decryption (commands 7/8/9)
| 0x00 || 4 || Mode: must be 4 for encryption, 5 for decryption
|-
| 0x04 || 8 || Unused
|-
|-
| 0x0C || 1 || Only used by commands 4/7: keyseed
| 0x04 || 8 || Unknown (0?)
|-
|-
|0x0D
| 0x0C || 4 || Keyseed
|1
|Submode: the 3 LSBs are 0 for commands 4/7, 1 for commands 5/8 and 2 for commands 6/9
|-
|0x0E
|2
|Unused
|-
|-
| 0x10 || 4 || Size of the following data
| 0x10 || 4 || Size of the following data
|-
|0x14
|16
|Only for commands 6/9: additional key
|-
|0x24
|16
|Only for command 9: reencrypted encryption key
|}
|}


=== Commands 4/7 ===
== Command 11: SHA1 ==
The behavior of these commands is:
 
# Read the header
# Verify the mode and submode match the current command
# Read the body size and check that it is non-zero
# Get the AES key at key slot 4 + <keyseed>. Command 4 can only encrypt with keyseeds 0..0x3F while command 7 can decrypt with keyseeds 0..0x7F.
# Derive the key for some keyseeds using per-console parameters:
## If the key mesh's derivation key MSB is 1 and keyseed is in the 0x20..0x2f or 0x6c, 0x7b range, invert the bits of the last word (4 bytes) of the key
## If the keyseed is in the 0x27..0x2f or 0x73..0x7b range, XOR the first word of the key with the key mesh derivation key
# For command 4, copy the input header to the output, just replacing mode 4 with 5, and encrypt the body from offset 0x14 using AES-CBC with a null IV and the key determined at step 5.
# For command 7, decrypt the data and output it without a header
 
=== Commands 5/8 ===
The behavior of these commands is identical to commands 4/7, except it uses per-console key computed from the key mesh with seed 1.
 
=== Commands 6/9 ===
For both commands, steps 1-3 are the same as above, but differ afterwards.
 
Command 6 works like this:
 
# Copy the 0x24-byte long header to the output, just replacing the mode from 4 to 5
# Generate a random buffer and encrypt it using per-console key with seed 2. Write the result of the operation at 0x24.
# Encrypt the random buffer using the key at 0x14
# Use the result of step 3 to encrypt the data, then output it
 
Command 9 is the logical counterpart:
 
# Decrypt data at 0x24 with the per-console key with seed 2
# Reencrypt the data of the previous step with the key located at offset 0x14
# Decrypt the data using the result of step 2 as a key
 
== Command 0xB: SHA1 ==


This command computes the SHA1 of the input. The input must be prefixed with a 4-byte header giving the length of the buffer. Output is 0x14-byte long.
This command computes the SHA1 of the input. The input must be prefixed with a 4-byte header giving the length of the buffer. Output is 0x14-byte long.


== Command 0xC: ECDSA key pair generation ==
== Command 12: ECDSA key pair generation ==


This command generates a random private key and computes the associated public key. See above for the parameters of the elliptic curve.
This command generates a random private key and computes the associated public key. See above for the parameters of the elliptic curve.
Line 914: Line 613:
*0x28 - Public Key point y value
*0x28 - Public Key point y value


== Command 0xD: ECDSA point multiplication ==
== Command 13: ECDSA point multiplication ==


This command multiplies an elliptic curve point by a scalar. See above for the parameters of the elliptic curve.
This command multiplies an elliptic curve point by a scalar. See above for the parameters of the elliptic curve.
Line 927: Line 626:
*0x14 - point y value (kP).y
*0x14 - point y value (kP).y


The result is a new point (x and y are each 0x14 bytes long).
The result is a new point(x and y are each 0x14 bytes long).


== Command 0xE: PRNG ==
== Command 14: PRNG ==


This function takes no input and generates an ECDSA private key similarly to command 12, but without computing the associated public key. (This is basically getting random data, but within the range given by the order of the curve.)
This function takes no input and generates random data of the given size (depending on the specified size of the output buffer).


== Command 0xF: Seed RNG buffer ==
== Command 15: Init Fuse Seeds ==
This function seeds the Kirk 32-byte RNG buffer used to generate all the random data coming from Kirk.
Kirk initialization of Fuse Seeds. Takes a 0x1C data as input and 0x1C data as output.


It takes as an input and output data of size 0x1c:
== Command 16: ECDSA signature generation ==


* 0x00 - 64-bit counter - increased by 1 in the output
This command generates an ECDSA signature of a SHA1 hash (0x14 buffer) using an encrypted private key.
* 0x08 - seed data (0x14 bytes long) - used for seeding as an input, and contains fresh reseeded data for the output


Seeding works this way:
Input is:
*0x00: 0x20-byte long encrypted buffer containing the private key
*0x20: the message hash.


# Increment the input counter
The output is a 0x28-byte long signature (r and s, both 0x14-byte long).
# Set the first 0x14 bytes of the PRNG seed to the input seed data, XOR'ed with a SHA1 of data coming from a true random number generator
# Initialize the 32-byte RNG buffer to two empty words, and two words taken from the input data at offsets 0x00 and 0x04
# Do a reseeding (see below)
# Output the bytes contained in the first 0x14 bytes of the PRNG seed after the reseeding


Reseeding is then done by all operations requiring random data and works this way:
The private key buffer is encrypted with a device-specific encryption using the FuseID.


# Encrypt RNG buffer with AES per-console key with seed 6
Here is the code of the decryption, thanks to Davee & Proxima. g_fuse90 and g_fuse94 are the two words composing the FuseID (present at the 0xBC100090 and 0xBC100094 hardware registers).
# Set the last half of the PRNG seed (0x14 bytes) to the contents RNG buffer
# Regenerate data with the PRNG
The functions requiring random data then use some parts of the PRNG state ("seed" (first 0x28 bytes of the PRNG state) or "result" (last 0x14 bytes of the PRNG state)) as random data to be used.


== Command 0x10: ECDSA signature generation ==
Output is 0x20-byte long, but the last 0xC bytes are ignored (and possibly always equal to zero) for the private key.


This command generates an ECDSA signature of a SHA1 hash (0x14 buffer) using an encrypted private key. It is used to verify IdStorage IDPS certificates.
<pre>
void decrypt_kirk16_private(u8 *dA_out, u8 *dA_enc)
  int i, k;
  kirk16_data keydata;
  u8 subkey_1[0x10], subkey_2[0x10];
  rijndael_ctx aes_ctx;
 
  keydata.fuseid[7] = g_fuse90 &0xFF;
  keydata.fuseid[6] = (g_fuse90>>8) &0xFF;
  keydata.fuseid[5] = (g_fuse90>>16) &0xFF;
  keydata.fuseid[4] = (g_fuse90>>24) &0xFF;
  keydata.fuseid[3] = g_fuse94 &0xFF;
  keydata.fuseid[2] = (g_fuse94>>8) &0xFF;
  keydata.fuseid[1] = (g_fuse94>>16) &0xFF;
  keydata.fuseid[0] = (g_fuse94>>24) &0xFF;
  /* set encryption key */
  rijndael_set_key(&aes_ctx, kirk16_key, 128);
 
  /* set the subkeys */
  for (i = 0; i < 0x10; i++)
  {
    /* set to the fuseid */
    subkey_2[i] = subkey_1[i] = keydata.fuseid[i % 8];
  }


Input is:
  /* do aes crypto */
*0x00: 0x20-byte long encrypted buffer containing the private key
  for (i = 0; i < 3; i++)
*0x20: the message hash.
  {
    /* encrypt + decrypt */
    rijndael_encrypt(&aes_ctx, subkey_1, subkey_1);
    rijndael_decrypt(&aes_ctx, subkey_2, subkey_2);
  }
 
  /* set new key */
  rijndael_set_key(&aes_ctx, subkey_1, 128);
 
  /* now lets make the key mesh */
  for (i = 0; i < 3; i++)
  {
    /* do encryption in group of 3 */
    for (k = 0; k < 3; k++)
    {
      /* crypto */
      rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
    }
 
    /* copy to out block */
    memcpy(&keydata.mesh[i * 0x10], subkey_2, 0x10);
  }
 
  /* set the key to the mesh */
  rijndael_set_key(&aes_ctx, &keydata.mesh[0x20], 128);
 
  /* do the encryption routines for the aes key */
  for (i = 0; i < 2; i++)
  {
    /* encrypt the data */
    rijndael_encrypt(&aes_ctx, &keydata.mesh[0x10], &keydata.mesh[0x10]);
  }


The output is a 0x28-byte long signature (r and s, both 0x14-byte long).
  /* set the key to that mesh shit */
  rijndael_set_key(&aes_ctx, &keydata.mesh[0x10], 128);


The private key buffer is encrypted with the per-console key with seed 3. The command simply decrypts it, verifies that the scalar is valid (non-zero and less than the order of the curve), and outputs the resulting signature.
  /* cbc decrypt the dA */
  AES_cbc_decrypt((AES_ctx *)&aes_ctx, dA_enc, dA_out, 0x20);
}
</pre>


== Command 0x11: ECDSA signature verification ==
== Command 17: ECDSA signature verification ==


This command verifies an ECDSA signature. It is used to verify IdStorage IDPS certificates.
This command verifies an ECDSA signature using the ECDSA curve described above.


It takes no output, and takes as an input:
It takes no output, and takes as an input:
Line 978: Line 731:
* 0x50: signature s
* 0x50: signature s


The result of the operation is given by the return value (0 on success, KIRK_ECDSA_DATA_INVALID on failure to verify the signature).
The result of the operation is given by the return value (0 on success, 5 on failure to verify the signature).
 
== Command 0x12: verify certificate ==
 
This command verifies an AES-CBC-MAC (OMAC1) signature. It is used to verify [[IDStorage#IDStorage_certified_sections|ID Storage certificates]].  


This command has no output.
== Command 18: verify certificate ==


It takes as input an [[IDStorage#IDStorage_certified_sections|ID Storage certificate]] read from [[IDStorage]].
This command has most likely no output header.


<source lang="C">
It takes as an input a 0xB8-long buffer:
typedef struct kirk_command_0x12_input{
*0x00: certificate data (either ConsoleID or OpenPSID)
ids_cert_psp certificate;
*0x10: certificate public key (x and y)
} kirk_command_0x12_input;
*0x38: ECDSA signature (r and s)
</source>
*0x60: ECDSA public key used for the signature
*0x88: certificate encrypted private key (padded)
*0xA8: AES-CMAC hash of the rest of the header.


It uses per-console key with seed 4.
Details are on PS Vita wiki. See also DespertarDelCementerio and CEX2DEX programs source codes.


= Error codes =
= Error codes =
Line 1,002: Line 753:
     0×01: Kirk not enabled
     0×01: Kirk not enabled
     0×02: Invalid mode
     0×02: Invalid mode
     0×03: Invalid header signature
     0×03: Invalid header digest
     0×04: Invalid data signature
     0×04: Invalid data digest
     0×05: Invalid ECDSA data
     0×05: Invalid signature
     0x0C: Kirk not seeded
     0x0C: isInCriticalSection violation
     0x0D: Invalid operation (out of 1-18 range)
     0x0D: Invalid operation (out of 1-18 range)
     0x0E: Invalid encryption keyseed
     0x0E: Invalid seed/code (cipher operations)
     0x0F: Invalid decryption keyseed
     0x0F: Invalid ?header size? (cipher operations)
     0×10: Invalid data size (equals 0) (sign/cipher operations)
     0×10: Invalid data size (equals 0) (sign/cipher operations)
</pre>
</pre>
Line 1,025: Line 776:


* The private key corresponding to the latest version Bootrom public key is unknown.
* The private key corresponding to the latest version Bootrom public key is unknown.
* Keys related to Kirk commands 0, 2 and 3 are unknown. (See above for details.)
* Commands 0, 2, 3 are mostly unknown and need testing/documentation.
* The Kirk's internal PRNG is deterministic but its function is unknown.
* Elliptic curves have additional parameters specified in the code, which are unknown.
Please note that all contributions to PSP Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PSP Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)