Kirk: Difference between revisions

From PSP Developer wiki
Jump to navigation Jump to search
 
(72 intermediate revisions by 4 users not shown)
Line 1: Line 1:
The PSP KIRK Crypto Engine is a security hardware device that is embedded into the TACHYON main IC chip. It is a bus master and can DMA to/from main DDR RAM memory, operating independantly of the CPU. It is intefaced via memory mapped registers at base of 0xBDE00000 ([[SPOCK Crypto Engine]] on the other hand is mapped to 0xBDF00000). It is capable of performing AES encryption, decryption, SHA1 Hash, pseudo random number generation, and signature generation and verifications (ECDSA) and CMAC.
The PSP KIRK Crypto Engine is a security hardware device that is embedded into the TACHYON main IC chip. It is a bus master and can DMA to/from main DDR RAM memory, operating independantly of the CPU. It is interfaced via memory mapped registers at base of 0xBDE00000 ([[SPOCK Crypto Engine]] on the other hand is mapped to 0xBDF00000). It is capable of performing AES encryption, decryption, SHA1 Hash, pseudo random number generation, and signature generation and verifications (ECDSA) and CMAC.


All (or almost all) the static keys used by the engine (plus the private key for KIRK CMD1) have been found through the PS3 hacks or glitching and can be found on the [[Keys]] page.
Most of the static keys used by the engine (plus the private key for Kirk command 1, which is not present on the chip) have been found through the PS3 hacks or glitching and can be found on the [[Keys]] page.


= Invocation =
= Invocation =
Line 20: Line 20:
Both use the usual Weierstrass form.
Both use the usual Weierstrass form.


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


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


<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, 0x10, 0x11, and likely 0x12.
This curved is used for Kirk commands 0xC, 0xD, 0xE, 0x10 and 0x11.


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


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).
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 85: Line 85:
pt17 = ecpy.curves.Point(0xbc660611a70bd7f2d140a48215c096d11d2d4112, 0xf0e9379ac4e0d387c542d091349dd15169dd5a87, crv17)
pt17 = ecpy.curves.Point(0xbc660611a70bd7f2d140a48215c096d11d2d4112, 0xf0e9379ac4e0d387c542d091349dd15169dd5a87, crv17)


# verify the KIRK1 ECDSA private key
# verify the Kirk command 1 ECDSA private key
crv1_g = ecpy.curves.Point(0x2259ACEE15489CB096A882F0AE1CF9FD8EE5F8FA, 0x604358456D0A1CB2908DE90F27D75C82BEC108C0, crv1)
crv1_g = ecpy.curves.Point(0x2259ACEE15489CB096A882F0AE1CF9FD8EE5F8FA, 0x604358456D0A1CB2908DE90F27D75C82BEC108C0, crv1)
assert(crv1.mul_point(crv1.generator, 0xF392E26490B80FD889F2D9722C1F34D7274F983D) == pt1)
assert(crv1.mul_point(crv1.generator, 0xF392E26490B80FD889F2D9722C1F34D7274F983D) == pt1)
</pre>
</pre>
= Slotted Keys =
The KIRK ROM can access different keys which are slotted in what might be some kind of secure enclave. There are slots for both AES and ECDSA keys.
== AES slotted keys ==
{| class="wikitable"
|+
!Id
!Content
!Do we have it?
|-
|0
|KIRK command 0 (Kbooti from Devkit) decryption key
|No
|-
|1
|KIRK command 0 (Kbooti from Devkit) CMAC key
|No
|-
|2
|KIRK command 1 (IPL) decryption key
|Yes
|-
|3
|KIRK command 2 (DRM) decryption key
|No
|-
|4~0x83
|KIRK commands 4/7 decryption keys (128 possible ones)
|Yes
|}
== ECDSA slotted keys ==
Note: public keys take two slots (for both coordinates), and private keys take only one.
{| class="wikitable"
|+
!Id
!Content
!Do we have it?
|-
|0/1
|KIRK command 1 (IPL) public key (used to verify valid IPLs)
|Yes (including the private key!)
|-
|2/3
|KIRK command 2 (DRM) public key (used to verify data passed to KIRK command 2)
|No
|-
|4
|KIRK command 3 (DRM) private key (used by command 2 to sign data for command 3)
|No
|-
|5/6
|KIRK command 3 (DRM) public key (used by command 3 to verify data coming from command 2)
|No
|}
= PSP Individual 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 ===
{| class="wikitable"
|+
!Seed parameter
!Usage
|-
|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)
|-
|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
|}
== PSP Individual Key Mesh Certificate ==
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.
=== Structure ===
<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>
=== Algorithm ===
To generate the ScePspIndividualKeyMeshCert of a specific PSP, provided its [[Fuse ID]], execute the following code.
<source lang="C">
void gen_psp_individual_key_mesh_certificate_hash(ScePspIndividualKeyMeshCert *cert) {
  byte bVar1;
  uint uVar2;
  int iVar3;
  byte *pbVar4;
  uint uVar5;
  uint uVar6;
  byte *pbVar7;
  uint uVar8;
  byte bVar9;
  int idx;
  int offset;
  byte *pbVar11;
  byte local_60 [80];
  byte m [16];
  uint uVar10;
 
  pbVar11 = local_60;
  m[0] = 1;
  m[1] = 0xf;
  m[2] = 0x36;
  m[3] = 0x78;
  m[4] = 0x40;
  offset = 0;
  do {
    pbVar4 = cert + offset;
    pbVar7 = local_60 + offset;
    offset = offset + 1;
    *pbVar7 = *pbVar4;
  } while (offset < 0x3c);
  offset = 0x3c;
  do {
    pbVar7 = local_60 + offset;
    offset = offset + 1;
    *pbVar7 = 0;
  } while (offset < 0x40);
  offset = 0;
  do {
    bVar1 = *pbVar11;
    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;
}
void gen_psp_individual_key_mesh_certificate(SomeStructure *ss, byte *data_for_0x38, ScePspIndividualKeyMeshCert *cert) { 
  gen_psp_key_mesh(cert->key_mesh);
  for (int idx = 0; idx < 8; idx++)
    cert->fuse_id[idx] = ss[idx];
  for (int idx = 0; idx < 4; idx++)
    cert->reserved[idx] = data_for_0x38[idx];
  gen_psp_individual_key_mesh_certificate_hash(cert);
  return 0;
}
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>


= Commands =
= Commands =


On PSP there are 18 KIRK commands. On PSVita, there are these 18 commands plus some new commands to support bigger keys (192 bits for example). See [https://wiki.henkaku.xyz/vita/F00D_Commands#gcauthmgr_sm.self F00D commands].
On PSP there are 19 Kirk commands. On PSVita, there are these 19 commands plus some new commands to support bigger keys (192 bits for example). See [https://wiki.henkaku.xyz/vita/F00D_Commands#gcauthmgr_sm.self F00D commands].


KIRK functions are called with the same 5 arguments (outbuf, outbuf_size, inbuf, inbuf_size, service_number (which is the command ID)). Depending on the service number used, the expectations of the inbuf or outbuf vary and are detailed below.
Kirk commands are called with the same 5 arguments (outbuf, outbuf_size, inbuf, inbuf_size, service_number (which is the command ID)). Depending on the service number used, the expectations of the inbuf or outbuf vary and are detailed below.


== Table ==
== Table ==
Line 106: Line 489:
! scope="col"| Output size
! scope="col"| Output size
! scope="col"| Used in
! scope="col"| Used in
! scope="col"| Uses perconsole key fuse based algo?
! scope="col"| Uses slot key? (if yes, specify)
|-
| 0
| KIRK_CMD_DECRYPT_BOOTROM
| Decryption of the psp devkit kbooti bootrom (no inverse)
| encrypted kbooti size+0x12
| decrypted kbooti bootrom size
| tachsm.o
| {{no}}
| {{Slot0_AES_1_CMAC}}
|-
|-
| 1
| 1
Line 112: Line 506:
| buf_size+0x90
| buf_size+0x90
| buf_size
| buf_size
| memlmd, mesg_led
| memlmd, mesg_led, bootrom
| {{no}}
| {{Slot2_AES_CMAC}}
|-
|-
| 2
| 2
| KIRK_CMD_DNAS_ENCRYPT
| KIRK_CMD_DNAS_ENCRYPT
| Encrypt Operation for DNAS (inverse of cmd 3)
| Encrypt Operation for DNAS (inverse of command 3)
| buf_size+0x90
| buf_size+0x90
| buf_size
| buf_size
|  
| mesg_led
| {{yes}}
| {{Slot3_AES}}
|-
|-
| 3
| 3
| KIRK_CMD_DNAS_DECRYPT
| KIRK_CMD_DNAS_DECRYPT
| Decrypt Operation for DNAS (inverse of cmd 2)
| Decrypt Operation for DNAS (inverse of command 2)
| buf_size+0x90
| buf_size+0x90
| buf_size
| buf_size
|  
| mesg_led
| {{yes}}
| {{no}}
|-
|-
| 4
| 4
| KIRK_CMD_ENCRYPT_STATIC
| KIRK_CMD_ENCRYPT_STATIC
| Encrypt Operation (inverse of cmd 7) (key=static)
| Encrypt Operation (inverse of command 7) (key=static)
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| chnnlsv, memab
| chnnlsv, memab, openpsid
| {{no}}
| {{Slot4_AES}}
|-
|-
| 5
| 5
| KIRK_CMD_ENCRYPT_PERCONSOLE
| KIRK_CMD_ENCRYPT_PERCONSOLE
| Encrypt Operation (inverse of cmd 8) (key=per-console)
| Encrypt Operation (inverse of command 8) (key=per-console)
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| chnnlsv, psheet since PSP FW 2.81 for PGD, ?openpsid for IDS Certificates?
| openpsid, chnnlsv, psheet since PSP FW 2.81 for PGD, ?openpsid for IDPS Certificates?
| {{yes}}
| {{no}}
|-
|-
| 6
| 6
| KIRK_CMD_ENCRYPT_USER
| KIRK_CMD_ENCRYPT_USER
| Encrypt Operation (inverse of cmd 9) (key=user-defined)
| Encrypt Operation (inverse of command 9) (key=user-defined)
| ?
| buf_size+0x24
| ?
| buf_size+0x34
|  
| power, inside a kl4e blob, IPL (stage 2)
| {{yes}}
| {{no}}
|-
|-
| 7
| 7
| KIRK_CMD_DECRYPT_STATIC
| KIRK_CMD_DECRYPT_STATIC
| Decrypt Operation (inverse of cmd 4) (key=static)
| Decrypt Operation (inverse of command 4) (key=static)
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| memlmd, mesg_led,chnnlsv, memab
| memlmd, mesg_led,chnnlsv, memab, openpsid, bootrom
| {{no}}
| {{Slot4_AES}}
|-
|-
| 8
| 8
| KIRK_CMD_DECRYPT_PERCONSOLE
| KIRK_CMD_DECRYPT_PERCONSOLE
| Decrypt Operation (inverse of cmd 5) (key=per-console)
| Decrypt Operation (inverse of command 5) (key=per-console)
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| buf_size+0x14
| chnnlsv, psheet since PSP FW 2.81 for PGD
| memlmd, chnnlsv, psheet since PSP FW 2.81 for PGD
| {{yes}}
| {{no}}
|-
|-
| 9
| 9
| KIRK_CMD_DECRYPT_USER
| KIRK_CMD_DECRYPT_USER
| Decrypt Operation (inverse of cmd 6) (key=user-defined)
| Decrypt Operation (inverse of command 6) (key=user-defined)
| ?
| buf_size+0x34 (header + key)
| ?
| buf_size
|  
| power, inside a kl4e blob, IPL (stage 2)
| {{yes}}
| {{no}}
|-
|-
| 10 (0xA)
| 10 (0xA)
| KIRK_CMD_PRIV_SIGVRY
| KIRK_CMD_PRIV_SIGVRY
| Private Signature Verify (checks for private SCE sig)
| Private Signature Verify (checks for private SCE signature)
| buf_size+0x90
| buf_size+0x90
| 0
| 0
|  
|  
| {{yes}}
| {{no}}
|-
|-
| 11 (0xB)
| 11 (0xB)
Line 182: Line 596:
| buf_size >= 0x14
| buf_size >= 0x14
| 0x14
| 0x14
| memlmd, mesg_led, memab
| memlmd, mesg_led, memab, chkreg, openpsid, bootrom
| {{no}}
| {{no}}
|-
|-
| 12 (0xC)
| 12 (0xC)
Line 189: Line 605:
| 0
| 0
| 0x3C
| 0x3C
| memab
| memab, openpsid
| {{no}}
| {{no}}
|-
|-
| 13 (0xD)
| 13 (0xD)
Line 196: Line 614:
| 0x3C
| 0x3C
| 0x3C
| 0x3C
|  
| memab, openpsid
| {{no}}
| {{no}}
|-
|-
| 14 (0xE)
| 14 (0xE)
Line 203: Line 623:
| 0
| 0
| 0x14
| 0x14
| mesg_led, chnnlsv, memab, semawm
| mesg_led, chnnlsv, memab, semawm, openpsid
| {{no}}
| {{no}}
|-
|-
| 15 (0xF)
| 15 (0xF)
| KIRK_CMD_15
| KIRK_CMD_SEED
| (absolutely no idea – could be KIRK initialization or PRNG reseeding)
| Seed the Kirk internal RNG buffer
| 0
| 0x1C
| 0
| 0x1C
| IPL
| IPL
| {{yes}}
| {{no}}
|-
|-
| 16 (0x10)
| 16 (0x10)
Line 217: Line 641:
| 0x34
| 0x34
| 0x28
| 0x28
| memab
| memab, openpsid (used for IDStorage Certificates ECDSA)
| {{yes}}
| {{no}}
|-
|-
| 17 (0x11)
| 17 (0x11)
| KIRK_CMD_SIGVRY
| KIRK_CMD_SIGVRY
| Signature Verification (checks for generated signatures)
| ECDSA Signature Verification
| 0x64
| 0x64
| 0
| 0
| memab
| memab, memlmd, mesg_led, openpsid (checks for generated signatures, used for IDStorage Certificates ECDSA)
| {{no}}
| {{no}}
|-
|-
| 18 (0x12)
| 18 (0x12)
| KIRK_CMD_CERTVRY
| KIRK_CMD_CERTVRY
| Certificate Verification (IDStorage Certificates CMAC)
| Certificate Verification
| 0xB8
| 0xB8
| 0
| 0
| openpsid, memab
| openpsid, memab, chkreg (used for IDStorage Certificates AES-CMAC)
| {{yes}}
| {{no}}
|}
|}


== Command 1: decryption and authentication ==
== Command 0x0: 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.
{| class="wikitable"
|+
!Address
!Size
!Description
|-
|0x0
|16
|CMAC of the body, computed using AES slotted key 1
|-
|0x10
|2
|Size of the body
|-
|0x12
|...
|Body, encrypted using AES slotted key 0
|}
The command is very simple and acts as follows:
 
# Verify the command is run at an early stage
# Read the body size and check it's non-zero
# Verify the CMAC of the body using AES slotted key 1
# While computing the CMAC, verify the body size didn't change
# Decrypt body using AES slotted key 0
 
== Commands 0x1, 0x2, 0x3 & 0xA: decryption and authentication ==


=== Overview ===
=== Overview ===


This function is used to both decrypt and verify the signature of the IPL blocks.
These three functions take very similar inputs, as they all do signature verification and decryption.


There are two versions of this service: AES CMAC Verification, and ECDSA Verification. They use the header section of the input buffer slightly differently.
* Command 1 is used to decrypt the IPL blocks.
* 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 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).


In both cases, the total header length is 0x90. The first 0x60 bytes depend on the version. The last 0x30 bytes are the same in both cases:
There are two versions of this service: AES CMAC verification, and ECDSA verification. They use the header section of the input buffer slightly differently.


'''Metadata Header Structure (Length 0x30)''':
In both cases, the total header length is 0x90. The 0x10..0x60 bytes depend on the signature mode.
 
'''Metadata Header Structure (Length 0x90)''':
{| class="wikitable"
{| class="wikitable"
|-
|-
! Address !! Size !! Description
! Address !! Size !! Description
|-
|-
| 0x60  || 4    || Set to 1
|0x00
|0x10
|Decryption key, encrypted with another key depending on the command
|-
|-
| 0x64  || 4    || 0 indicates AES CMAC version, 1 indicates ECDSA version
|0x10
|0x50
|Signature information, depends on the signature mode (see below)
|-
|-
| 0x68  || 4    || 0
| 0x60  || 4    || Set to 1, 2 or 3 depending on the command
|-
| 0x64  || 4    || Bit 0 is 0 if block is AES CMAC-signed, 1 if it is ECDSA-signed
Bit 1 is used by command 2 to determine if the resulting Kirk 3 block should be AES CMAC-signed (0) or ECDSA-signed (1)
|-
| 0x68  || 4    || Bit 0 indicates all input data (including the full header) should be wiped if the body signature check fails
|-
|-
| 0x6C  || 4    || 0 for retail version and 0xFFFFFFFF for dev versions
| 0x6C  || 4    || 0 for retail version and 0xFFFFFFFF for dev versions
Line 261: Line 733:
| 0x74  || 4    || Length of the padding after the header and before the real data
| 0x74  || 4    || Length of the padding after the header and before the real data
|-
|-
| 0x78  || 8   || 0
| 0x78  || 24   || Unused
|}
|}


=== AES CMAC Version ===
=== AES CMAC Version ===


'''Key Header Structure (Length 0x60)''':
'''Signature Structure (Length 0x60)''':
{| class="wikitable"
{| class="wikitable"
|-
|-
! Address !! Size !! Description
! Address !! Size !! Description
|-
|-
| 0x00 || 16 || Decryption key, encrypted with the KIRK1 AES master key
| 0x10 || 16 || CMAC key, encrypted with the the same key as the decryption key (at 0x00)
|-
| 0x10 || 16 || CMAC key, encrypted with the KIRK1 AES master key
|-
|-
| 0x20 || 16 || Header hash (CMAC)
| 0x20 || 16 || Header hash (CMAC)
Line 282: Line 752:
|}
|}


==== Decryption process ====
==== Verification process ====


The first 0x20 bytes of the Key Header are decrypted with the KIRK 1 Stored AES Key. This was allegedly discovered by Datel by decapping the chip and reversing engineering the algorithms and keys. This was also recovered through the failure in PS3 cryptography by decrypting the isolated module in the PSP emulator on the PS3.
The CMAC key at 0x10 is decrypted using a key which depends on the command and is the same as the decryption key at 0x00 (see below). It is decrypted using AES-CBC (so offset 0x00 is used as the IV).


The first block is the AES Key used for decrypting the main data. The second block is used to decrypt the next two blocks (0x20 bytes at offset 0x20). These represent the Metadata Header CMAC and the Data CMAC. They are checked against the AES CMAC of the metadata header section and the AES CMAC of the whole data, from the metadata header section to the end of the data (including padding in-between).
The CMAC of the header from offset 0x60 and size 0x30 is computed. Kirk then checks the data size & offset (at 0x70 and 0x74) didn't change from what was previously read (possibly to avoid data being overwritten while being processed). The value is then checked against the value at 0x20.
 
If this fails, the command returns KIRK_HEADER_SIG_INVALID. Otherwise, except for command 10, it proceeds with the full data CMAC, computed from header offset 0x60 to the end of the body contents. The value is checked against the value at 0x30.
 
If this second check fails, and the LSB of 0x68 is set to 1, all the input data is wiped (set to zero's). In both cases, if the check fails, it then returns KIRK_HEADER_SIG_INVALID.


=== ECDSA Version ===
=== ECDSA Version ===
Line 294: Line 768:
|-
|-
! Address !! Size !! Description
! Address !! Size !! Description
|-
| 0x00 || 0x10 || Decryption key, encrypted with the KIRK1 AES master key
|-
|-
| 0x10 || 0x14 || Header ECDSA signature r
| 0x10 || 0x14 || Header ECDSA signature r
Line 306: Line 778:
|}
|}


==== Decryption process ====
==== Verification process ====
 
The ECDSA version is slightly different. The header from offset 0x60 with size 0x30 is hashed and the header signature is verified. Similarly to CMAC, it then verifies values at 0x70 and 0x74 didn't change. It then acts similarly to the CMAC version with the data signature, including the possible data wiping.


The ECDSA version is slightly different. Only the first block (0x10 bytes) is decrypted with the Kirk 1 AES Key. It is used to decrypt the main data section just as in the AES CMAC version. Rather than a CMAC, the Metadata header is checked by SHA1 hashing its 0x30 bytes and checking the signature components through a ECDSA Verify call. The encrypted Data section is also checked via SHA1 of the entire data through a ECDSA Verify call.
=== Commands 1 & 3 ===
Commands 1 and 3 work exactly the same. The only difference is that the ECDSA public key comes from slots 0/1 for command 1, and 5/6 for command 3. Also, the AES key, used for decrypting the decryption & CMAC keys, is a static key in keyslot 2 for command 1, and a per-console key with seed 0 for command 3.


The ECDSA curve parameters are indicated above.
# Verify that the command mode at 0x60 matches the current command
# Read the body size and data offset and verify that the body size is non-zero
# Get or compute the AES key
# Check the signature mode at 0x64, and check the header & the data signature as specified above depending on the signature mode
# Decrypt the decryption key at 0x00 using the key from step 3.
# Decrypt the data using AES-CBC with a null IV.


== Commands 2 & 3: DRM encrypt & decrypt ==
=== Command 2 ===
Command 2 is a bit more complicated as it re-encrypts data for command 3.


These commands are mostly unknown. The header is the same as KIRK command 1, with the mode set to 2 or 3.
# Follow steps 1-5 from above, using key slots 2/3 for the ECDSA key and key slot 3 for the AES key
# Copy the input header (including padding) to the output
# Change offset 0x60 (command) to command 3
# Change offset 0x64 to 0 or 1 depending on the second bit of the input value at 0x64 (which determines if the output of command 2 should be ECDSA or CMAC-signed)
# Decrypt the body of the data similarly to commands 1 & 3
# Generate a random key and encrypt it with per-console key (seed = 0), and store the result at 0x00
# If in CMAC mode for the output, do the same for the CMAC key at 0x10 (encrypt using CBC mode and data at 0x00 as the IV)
# Encrypt the body in CBC mode with a null IV
# 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).


In command 2, the input data passed to KIRK is first checked (presumably CMAC), then decrypted, and re-encrypted with the console unique private key.
=== Command 0xA ===
Having that common key would allow legit creation of DRM BB install packages.
Its behavior is very simple:


Command 3 is the decryption counterpart of command 2.
# Determine if the input is data for command 1, 2 or 3 depending on the command mode. (If it is another value, return an error.)
# Get or compute AES and ECDSA public keys depending on the command
# Check the signature similarly to the other commands.


== 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.
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.
- 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 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
- Commands 6 (encryption) and 9 (decryption) use a key derived from the keyseed using an unknown key derivation function


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, 5 for decryption
| 0x00 || 4 || Mode: must be 4 for encryption (commands 4/5/6), 5 for decryption (commands 7/8/9)
|-
| 0x04 || 8 || Unused
|-
| 0x0C || 1 || Only used by commands 4/7: keyseed
|-
|-
| 0x04 || 8 || Unknown (0?)
|0x0D
|1
|Submode: the 3 LSBs are 0 for commands 4/7, 1 for commands 5/8 and 2 for commands 6/9
|-
|-
| 0x0C || 4 || Keyseed
|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
|}
|}


== Command 10: AES CMAC verification ==
=== Commands 4/7 ===
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.


This seems to be the AES CMAC verification of CMD1, and takes the same header as CMD1, the only difference is that no decryption is performed.
=== Commands 6/9 ===
For both commands, steps 1-3 are the same as above, but differ afterwards.


See command 1 information for details.
Command 6 works like this:


It could also possibly verify CMACs for commands 2 & 3, but that is unknown.
# 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 11: SHA1 ==
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 12: ECDSA key pair generation ==
== Command 0xC: 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 363: Line 897:
*0x28 - Public Key point y value
*0x28 - Public Key point y value


== Command 13: ECDSA point multiplication ==
== Command 0xD: 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 376: Line 910:
*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 14: PRNG ==
== Command 0xE: PRNG ==


This function takes no input and generates random data of the given size (depending on the specified size of the output buffer).
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.)


== Command 15: init? ==
== Command 0xF: Seed RNG buffer ==
This function seeds the Kirk 32-byte RNG buffer used to generate all the random data coming from Kirk.


This function takes no input and no output.
It takes as an input and output data of size 0x1c:


It could be either a Kirk initialization, or a PRNG re-seeding.
* 0x00 - 64-bit counter - increased by 1 in the output
* 0x08 - seed data (0x14 bytes long) - used for seeding as an input, and contains fresh reseeded data for the output


== Command 16: ECDSA signature generation ==
Seeding works this way:


This command generates an ECDSA signature of a SHA1 hash (0x14 buffer) using an encrypted private key.
# Increment the input counter
# 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:
 
# Encrypt RNG buffer with AES per-console key with seed 6
# 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 ==
 
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.


Input is:
Input is:
Line 398: Line 949:
The output is a 0x28-byte long signature (r and s, both 0x14-byte long).
The output is a 0x28-byte long signature (r and s, both 0x14-byte long).


The private key buffer is encrypted with a device-specific encryption using the FuseID.
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.


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).
== Command 0x11: ECDSA signature verification ==
Output is 0x20-byte long, but the last 0xC bytes are ignored (and possibly always equal to zero) for the private key.


<pre>
This command verifies an ECDSA signature. It is used to verify IdStorage IDPS certificates.
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];
  }


  /* do aes crypto */
It takes no output, and takes as an input:
  for (i = 0; i < 3; i++)
* 0x00: public key
  {
* 0x28: signed message hash
    /* encrypt + decrypt */
* 0x3C: signature r
    rijndael_encrypt(&aes_ctx, subkey_1, subkey_1);
* 0x50: signature s
    rijndael_decrypt(&aes_ctx, subkey_2, subkey_2);
  }


  /* set new key */
The result of the operation is given by the return value (0 on success, KIRK_ECDSA_DATA_INVALID on failure to verify the signature).
  rijndael_set_key(&aes_ctx, subkey_1, 128);


  /* now lets make the key mesh */
== Command 0x12: verify certificate ==
  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 */
This command verifies an AES-CBC-MAC (OMAC1) signature. It is used to verify [[IDStorage#IDStorage_certified_sections|ID Storage certificates]].
    memcpy(&keydata.mesh[i * 0x10], subkey_2, 0x10);
  }


  /* set the key to the mesh */
This command has no output.
  rijndael_set_key(&aes_ctx, &keydata.mesh[0x20], 128);


  /* do the encryption routines for the aes key */
It takes as input an [[IDStorage#IDStorage_certified_sections|ID Storage certificate]] read from [[IDStorage]].
  for (i = 0; i < 2; i++)
  {
    /* encrypt the data */
    rijndael_encrypt(&aes_ctx, &keydata.mesh[0x10], &keydata.mesh[0x10]);
  }


  /* set the key to that mesh shit */
<source lang="C">
  rijndael_set_key(&aes_ctx, &keydata.mesh[0x10], 128);
typedef struct kirk_command_0x12_input{
ids_cert_psp certificate;
} kirk_command_0x12_input;
</source>


  /* cbc decrypt the dA */
It uses per-console key with seed 4.
  AES_cbc_decrypt((AES_ctx *)&aes_ctx, dA_enc, dA_out, 0x20);
}
</pre>


== Command 17: ECDSA signature verification ==
= Error codes =


This command verifies an ECDSA signature using the ECDSA curve described above.
<pre>
 
    0×00: Success
It takes no output, and takes as an input:
    0×01: Kirk not enabled
* 0x00: public key
    0×02: Invalid mode
* 0x28: signed message hash
    0×03: Invalid header signature
* 0x3C: signature r
    0×04: Invalid data signature
* 0x50: signature s
    0×05: Invalid ECDSA data
 
    0x0C: Kirk not seeded
The result of the operation is given by the return value (0 on success, 5 on failure to verify the signature).
    0x0D: Invalid operation (out of 1-18 range)
 
    0x0E: Invalid encryption keyseed
== Command 18 ==
    0x0F: Invalid decryption keyseed
 
    0×10: Invalid data size (equals 0) (sign/cipher operations)
Unknown.
</pre>


= Code Samples =
= Code Samples =
Line 501: Line 1,007:
= Open problems =
= Open problems =


* The private key corresponding to the latest version PRE-IPL public key is unknown.
* The private key corresponding to the latest version Bootrom public key is unknown.
* Commands 2, 3, 5, 6, 8, 9 & 18 (and partly 10) are mostly unknown and need testing/documentation.
* Keys related to Kirk commands 0, 2 and 3 are unknown. (See above for details.)
* The Kirk's internal PRNG is deterministic but its function is unknown.
* Elliptic curves have additional parameters specified in the code, which are unknown.

Latest revision as of 01:38, 19 January 2024

The PSP KIRK Crypto Engine is a security hardware device that is embedded into the TACHYON main IC chip. It is a bus master and can DMA to/from main DDR RAM memory, operating independantly of the CPU. It is interfaced via memory mapped registers at base of 0xBDE00000 (SPOCK Crypto Engine on the other hand is mapped to 0xBDF00000). It is capable of performing AES encryption, decryption, SHA1 Hash, pseudo random number generation, and signature generation and verifications (ECDSA) and CMAC.

Most of the static keys used by the engine (plus the private key for Kirk command 1, which is not present on the chip) have been found through the PS3 hacks or glitching and can be found on the Keys page.

Invocation[edit | edit source]

All of the Kirk commands can be used using the function sceUtilsBufferCopyWithRange, which takes five arguments:

  • the output buffer (if there is one, NULL otherwise)
  • the output buffer size (if there is one, 0 otherwise)
  • the input buffer (if there is one, NULL otherwise)
  • the input buffer size (if there is one, 0 otherwise)
  • the index of the command (as detailed below).

Elliptic curves[edit | edit source]

The PSP uses ECDSA for public-key cryptography. Elliptic curves are known for being fast and only requiring small keys, contrary to other public-key cryptography algorithms. They are still considered to be very secure, even for the 160-bit curves used by the PSP, unless a mistake is made when using them.

These curves have been designed by Sony only for the console. They are not vulnerable to any known attack.

Both use the usual Weierstrass form.

Elliptic curve for Kirk commands 1/2/3/0xA[edit | edit source]

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

p = 0xFFFFFFFFFFFFFFFF0001B5C617F290EAE1DBAD8F
G = (0x2259ACEE15489CB096A882F0AE1CF9FD8EE5F8FA, 0x604358456D0A1CB2908DE90F27D75C82BEC108C0)
n = 0xFFFFFFFFFFFFFFFF00000001FFFFFFFFFFFFFFFF
a = -3
b = 0x65D1488C0359E234ADC95BD3908014BD91A525F9

The public key is hardcoded, and is equal to: (0xED9CE58234E61A53C685D64D51D0236BC3B5D4B9, 0x049DF1A075C0E04FB344858B61B79B69A63D2C39).

Elliptic curve for the other commands[edit | edit source]

This curved is used for Kirk commands 0xC, 0xD, 0xE, 0x10 and 0x11.

p = 0xFFFFFFFFFFFFFFFEFFFFB5AE3C523E63944F2127
G = (0x128EC4256487FD8FDF64E2437BC0A1F6D5AFDE2C, 0x5958557EB1DB001260425524DBC379D5AC5F4ADF)
n = 0xFFFFFFFFFFFFFFFF00000001FFFFFFFFFFFFFFFF
a = -3
b = 0xA68BEDC33418029C1D3CE33B9A321FCCBB9E0F0B

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[edit | edit source]

Below is an example of how to manipulate these curves using the ecpy python library.

import ecpy.curves

psp_curve_cmd1 = {
    'name':      "psp_curve_cmd1",
    'type':      "weierstrass",
    'size':      160,
    'field':     0xFFFFFFFFFFFFFFFF00000001FFFFFFFFFFFFFFFF,
    'generator': (0x2259ACEE15489CB096A882F0AE1CF9FD8EE5F8FA, 0x604358456D0A1CB2908DE90F27D75C82BEC108C0),
    'order':     0xFFFFFFFFFFFFFFFF0001B5C617F290EAE1DBAD8F,
    'cofactor':  1,
    'a':         -3,
    'b':         0x65D1488C0359E234ADC95BD3908014BD91A525F9,
}

psp_curve_cmd17 = {
    'name':      "psp_curve_cmd17",
    'type':      "weierstrass",
    'size':      160,
    'field':     0xFFFFFFFFFFFFFFFF00000001FFFFFFFFFFFFFFFF,
    'generator': (0x128EC4256487FD8FDF64E2437BC0A1F6D5AFDE2C, 0x5958557EB1DB001260425524DBC379D5AC5F4ADF),
    'order':     0xFFFFFFFFFFFFFFFEFFFFB5AE3C523E63944F2127,
    'cofactor':  1,
    'a':         -3,
    'b':         0xA68BEDC33418029C1D3CE33B9A321FCCBB9E0F0B,
}

crv1 = ecpy.curves.WeierstrassCurve(psp_curve_cmd1)
crv17 = ecpy.curves.WeierstrassCurve(psp_curve_cmd17)

pt1 = ecpy.curves.Point(0xED9CE58234E61A53C685D64D51D0236BC3B5D4B9, 0x049DF1A075C0E04FB344858B61B79B69A63D2C39, crv1)
pt17 = ecpy.curves.Point(0xbc660611a70bd7f2d140a48215c096d11d2d4112, 0xf0e9379ac4e0d387c542d091349dd15169dd5a87, crv17)

# verify the Kirk command 1 ECDSA private key
crv1_g = ecpy.curves.Point(0x2259ACEE15489CB096A882F0AE1CF9FD8EE5F8FA, 0x604358456D0A1CB2908DE90F27D75C82BEC108C0, crv1)
assert(crv1.mul_point(crv1.generator, 0xF392E26490B80FD889F2D9722C1F34D7274F983D) == pt1)

Slotted Keys[edit | edit source]

The KIRK ROM can access different keys which are slotted in what might be some kind of secure enclave. There are slots for both AES and ECDSA keys.

AES slotted keys[edit | edit source]

Id Content Do we have it?
0 KIRK command 0 (Kbooti from Devkit) decryption key No
1 KIRK command 0 (Kbooti from Devkit) CMAC key No
2 KIRK command 1 (IPL) decryption key Yes
3 KIRK command 2 (DRM) decryption key No
4~0x83 KIRK commands 4/7 decryption keys (128 possible ones) Yes

ECDSA slotted keys[edit | edit source]

Note: public keys take two slots (for both coordinates), and private keys take only one.

Id Content Do we have it?
0/1 KIRK command 1 (IPL) public key (used to verify valid IPLs) Yes (including the private key!)
2/3 KIRK command 2 (DRM) public key (used to verify data passed to KIRK command 2) No
4 KIRK command 3 (DRM) private key (used by command 2 to sign data for command 3) No
5/6 KIRK command 3 (DRM) public key (used by command 3 to verify data coming from command 2) No

PSP Individual Keys[edit | edit source]

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[edit | edit source]

Structure[edit | edit source]

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;

Algorithm[edit | edit source]

To generate the individual key mesh of a specific PSP, provided its Fuse ID, execute the following code.

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

Final PSP Individual Keys[edit | edit source]

Algorithm[edit | edit source]

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.

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

Seed Parameter Per Command[edit | edit source]

Seed parameter Usage
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)
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

PSP Individual Key Mesh Certificate[edit | edit source]

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.

Structure[edit | edit source]

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;

Algorithm[edit | edit source]

To generate the ScePspIndividualKeyMeshCert of a specific PSP, provided its Fuse ID, execute the following code.

void gen_psp_individual_key_mesh_certificate_hash(ScePspIndividualKeyMeshCert *cert) {
  byte bVar1;
  uint uVar2;
  int iVar3;
  byte *pbVar4;
  uint uVar5;
  uint uVar6;
  byte *pbVar7;
  uint uVar8;
  byte bVar9;
  int idx;
  int offset;
  byte *pbVar11;
  byte local_60 [80];
  byte m [16];
  uint uVar10;
  
  pbVar11 = local_60;
  m[0] = 1;
  m[1] = 0xf;
  m[2] = 0x36;
  m[3] = 0x78;
  m[4] = 0x40;

  offset = 0;
  do {
    pbVar4 = cert + offset;
    pbVar7 = local_60 + offset;
    offset = offset + 1;
    *pbVar7 = *pbVar4;
  } while (offset < 0x3c);

  offset = 0x3c;
  do {
    pbVar7 = local_60 + offset;
    offset = offset + 1;
    *pbVar7 = 0;
  } while (offset < 0x40);

  offset = 0;
  do {
    bVar1 = *pbVar11;

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

void gen_psp_individual_key_mesh_certificate(SomeStructure *ss, byte *data_for_0x38, ScePspIndividualKeyMeshCert *cert) {   
  gen_psp_key_mesh(cert->key_mesh);

  for (int idx = 0; idx < 8; idx++)
    cert->fuse_id[idx] = ss[idx];

  for (int idx = 0; idx < 4; idx++)
    cert->reserved[idx] = data_for_0x38[idx];

  gen_psp_individual_key_mesh_certificate_hash(cert);

  return 0;
}

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

Commands[edit | edit source]

On PSP there are 19 Kirk commands. On PSVita, there are these 19 commands plus some new commands to support bigger keys (192 bits for example). See F00D commands.

Kirk commands are called with the same 5 arguments (outbuf, outbuf_size, inbuf, inbuf_size, service_number (which is the command ID)). Depending on the service number used, the expectations of the inbuf or outbuf vary and are detailed below.

Table[edit | edit source]

Command ID Name Short description Input size Output size Used in Uses perconsole key fuse based algo? Uses slot key? (if yes, specify)
0 KIRK_CMD_DECRYPT_BOOTROM Decryption of the psp devkit kbooti bootrom (no inverse) encrypted kbooti size+0x12 decrypted kbooti bootrom size tachsm.o No Slot0_AES_1_CMAC
1 KIRK_CMD_DECRYPT_PRIVATE Super-Duper decryption (no inverse) buf_size+0x90 buf_size memlmd, mesg_led, bootrom No Slot2_AES_CMAC
2 KIRK_CMD_DNAS_ENCRYPT Encrypt Operation for DNAS (inverse of command 3) buf_size+0x90 buf_size mesg_led Yes Slot3_AES
3 KIRK_CMD_DNAS_DECRYPT Decrypt Operation for DNAS (inverse of command 2) buf_size+0x90 buf_size mesg_led Yes No
4 KIRK_CMD_ENCRYPT_STATIC Encrypt Operation (inverse of command 7) (key=static) buf_size+0x14 buf_size+0x14 chnnlsv, memab, openpsid No Slot4<->0x83_AES
5 KIRK_CMD_ENCRYPT_PERCONSOLE Encrypt Operation (inverse of command 8) (key=per-console) buf_size+0x14 buf_size+0x14 openpsid, chnnlsv, psheet since PSP FW 2.81 for PGD, ?openpsid for IDPS Certificates? Yes No
6 KIRK_CMD_ENCRYPT_USER Encrypt Operation (inverse of command 9) (key=user-defined) buf_size+0x24 buf_size+0x34 power, inside a kl4e blob, IPL (stage 2) Yes No
7 KIRK_CMD_DECRYPT_STATIC Decrypt Operation (inverse of command 4) (key=static) buf_size+0x14 buf_size+0x14 memlmd, mesg_led,chnnlsv, memab, openpsid, bootrom No Slot4<->0x83_AES
8 KIRK_CMD_DECRYPT_PERCONSOLE Decrypt Operation (inverse of command 5) (key=per-console) buf_size+0x14 buf_size+0x14 memlmd, chnnlsv, psheet since PSP FW 2.81 for PGD Yes No
9 KIRK_CMD_DECRYPT_USER Decrypt Operation (inverse of command 6) (key=user-defined) buf_size+0x34 (header + key) buf_size power, inside a kl4e blob, IPL (stage 2) Yes No
10 (0xA) KIRK_CMD_PRIV_SIGVRY Private Signature Verify (checks for private SCE signature) buf_size+0x90 0 Yes No
11 (0xB) KIRK_CMD_HASH SHA1 Hash buf_size >= 0x14 0x14 memlmd, mesg_led, memab, chkreg, openpsid, bootrom No No
12 (0xC) KIRK_CMD_ECDSA_GENKEY ECDSA Generate Private/Public Key Pair 0 0x3C memab, openpsid No No
13 (0xD) KIRK_CMD_ECDSA_MUL ECDSA Multiply Point 0x3C 0x3C memab, openpsid No No
14 (0xE) KIRK_CMD_PRNGEN Pseudo Random Number Generation 0 0x14 mesg_led, chnnlsv, memab, semawm, openpsid No No
15 (0xF) KIRK_CMD_SEED Seed the Kirk internal RNG buffer 0x1C 0x1C IPL Yes No
16 (0x10) KIRK_CMD_SIGGEN ECDSA Signature Generation 0x34 0x28 memab, openpsid (used for IDStorage Certificates ECDSA) Yes No
17 (0x11) KIRK_CMD_SIGVRY ECDSA Signature Verification 0x64 0 memab, memlmd, mesg_led, openpsid (checks for generated signatures, used for IDStorage Certificates ECDSA) No No
18 (0x12) KIRK_CMD_CERTVRY Certificate Verification 0xB8 0 openpsid, memab, chkreg (used for IDStorage Certificates AES-CMAC) Yes No

Command 0x0: decrypt kbooti[edit | edit source]

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.

Address Size Description
0x0 16 CMAC of the body, computed using AES slotted key 1
0x10 2 Size of the body
0x12 ... Body, encrypted using AES slotted key 0

The command is very simple and acts as follows:

  1. Verify the command is run at an early stage
  2. Read the body size and check it's non-zero
  3. Verify the CMAC of the body using AES slotted key 1
  4. While computing the CMAC, verify the body size didn't change
  5. Decrypt body using AES slotted key 0

Commands 0x1, 0x2, 0x3 & 0xA: decryption and authentication[edit | edit source]

Overview[edit | edit source]

These three functions take very similar inputs, as they all do signature verification and decryption.

  • Command 1 is used to decrypt the IPL blocks.
  • 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 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).

There are two versions of this service: AES CMAC verification, and ECDSA verification. They use the header section of the input buffer slightly differently.

In both cases, the total header length is 0x90. The 0x10..0x60 bytes depend on the signature mode.

Metadata Header Structure (Length 0x90):

Address Size Description
0x00 0x10 Decryption key, encrypted with another key depending on the command
0x10 0x50 Signature information, depends on the signature mode (see below)
0x60 4 Set to 1, 2 or 3 depending on the command
0x64 4 Bit 0 is 0 if block is AES CMAC-signed, 1 if it is ECDSA-signed

Bit 1 is used by command 2 to determine if the resulting Kirk 3 block should be AES CMAC-signed (0) or ECDSA-signed (1)

0x68 4 Bit 0 indicates all input data (including the full header) should be wiped if the body signature check fails
0x6C 4 0 for retail version and 0xFFFFFFFF for dev versions
0x70 4 Length of decrypted data
0x74 4 Length of the padding after the header and before the real data
0x78 24 Unused

AES CMAC Version[edit | edit source]

Signature Structure (Length 0x60):

Address Size Description
0x10 16 CMAC key, encrypted with the the same key as the decryption key (at 0x00)
0x20 16 Header hash (CMAC)
0x30 16 Data hash (CMAC)
0x40 32 0

Verification process[edit | edit source]

The CMAC key at 0x10 is decrypted using a key which depends on the command and is the same as the decryption key at 0x00 (see below). It is decrypted using AES-CBC (so offset 0x00 is used as the IV).

The CMAC of the header from offset 0x60 and size 0x30 is computed. Kirk then checks the data size & offset (at 0x70 and 0x74) didn't change from what was previously read (possibly to avoid data being overwritten while being processed). The value is then checked against the value at 0x20.

If this fails, the command returns KIRK_HEADER_SIG_INVALID. Otherwise, except for command 10, it proceeds with the full data CMAC, computed from header offset 0x60 to the end of the body contents. The value is checked against the value at 0x30.

If this second check fails, and the LSB of 0x68 is set to 1, all the input data is wiped (set to zero's). In both cases, if the check fails, it then returns KIRK_HEADER_SIG_INVALID.

ECDSA Version[edit | edit source]

Key Header Structure (Length 0x60):

Address Size Description
0x10 0x14 Header ECDSA signature r
0x24 0x14 Header ECDSA signature s
0x38 0x14 Data ECDSA signature r
0x4C 0x14 Data ECDSA signature s

Verification process[edit | edit source]

The ECDSA version is slightly different. The header from offset 0x60 with size 0x30 is hashed and the header signature is verified. Similarly to CMAC, it then verifies values at 0x70 and 0x74 didn't change. It then acts similarly to the CMAC version with the data signature, including the possible data wiping.

Commands 1 & 3[edit | edit source]

Commands 1 and 3 work exactly the same. The only difference is that the ECDSA public key comes from slots 0/1 for command 1, and 5/6 for command 3. Also, the AES key, used for decrypting the decryption & CMAC keys, is a static key in keyslot 2 for command 1, and a per-console key with seed 0 for command 3.

  1. Verify that the command mode at 0x60 matches the current command
  2. Read the body size and data offset and verify that the body size is non-zero
  3. Get or compute the AES key
  4. Check the signature mode at 0x64, and check the header & the data signature as specified above depending on the signature mode
  5. Decrypt the decryption key at 0x00 using the key from step 3.
  6. Decrypt the data using AES-CBC with a null IV.

Command 2[edit | edit source]

Command 2 is a bit more complicated as it re-encrypts data for command 3.

  1. Follow steps 1-5 from above, using key slots 2/3 for the ECDSA key and key slot 3 for the AES key
  2. Copy the input header (including padding) to the output
  3. Change offset 0x60 (command) to command 3
  4. Change offset 0x64 to 0 or 1 depending on the second bit of the input value at 0x64 (which determines if the output of command 2 should be ECDSA or CMAC-signed)
  5. Decrypt the body of the data similarly to commands 1 & 3
  6. Generate a random key and encrypt it with per-console key (seed = 0), and store the result at 0x00
  7. If in CMAC mode for the output, do the same for the CMAC key at 0x10 (encrypt using CBC mode and data at 0x00 as the IV)
  8. Encrypt the body in CBC mode with a null IV
  9. 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[edit | edit source]

Its behavior is very simple:

  1. Determine if the input is data for command 1, 2 or 3 depending on the command mode. (If it is another value, return an error.)
  2. Get or compute AES and ECDSA public keys depending on the command
  3. Check the signature similarly to the other commands.

Commands 4~9: AES encrypt & decrypt[edit | edit source]

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.

- 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

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

Address Size Description
0x00 4 Mode: must be 4 for encryption (commands 4/5/6), 5 for decryption (commands 7/8/9)
0x04 8 Unused
0x0C 1 Only used by commands 4/7: keyseed
0x0D 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
0x14 16 Only for commands 6/9: additional key
0x24 16 Only for command 9: reencrypted encryption key

Commands 4/7[edit | edit source]

The behavior of these commands is:

  1. Read the header
  2. Verify the mode and submode match the current command
  3. Read the body size and check that it is non-zero
  4. 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.
  5. Derive the key for some keyseeds using per-console parameters:
    1. 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
    2. 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
  6. 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.
  7. For command 7, decrypt the data and output it without a header

Commands 5/8[edit | edit source]

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[edit | edit source]

For both commands, steps 1-3 are the same as above, but differ afterwards.

Command 6 works like this:

  1. Copy the 0x24-byte long header to the output, just replacing the mode from 4 to 5
  2. Generate a random buffer and encrypt it using per-console key with seed 2. Write the result of the operation at 0x24.
  3. Encrypt the random buffer using the key at 0x14
  4. Use the result of step 3 to encrypt the data, then output it

Command 9 is the logical counterpart:

  1. Decrypt data at 0x24 with the per-console key with seed 2
  2. Reencrypt the data of the previous step with the key located at offset 0x14
  3. Decrypt the data using the result of step 2 as a key

Command 0xB: SHA1[edit | edit source]

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[edit | edit source]

This command generates a random private key and computes the associated public key. See above for the parameters of the elliptic curve.

This returns the following into the keypair buffer, of size 0x3C (each value is 0x14 bytes long):

  • 0x00 - randomly generated private key
  • 0x14 - Public Key point x value
  • 0x28 - Public Key point y value

Command 0xD: ECDSA point multiplication[edit | edit source]

This command multiplies an elliptic curve point by a scalar. See above for the parameters of the elliptic curve.

Input (size 0x3c):

  • 0x00 - scalar k
  • 0x14 - point x value P.x
  • 0x28 - point y value P.y

Output (size 0x28):

  • 0x00 - point x value (kP).x
  • 0x14 - point y value (kP).y

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

Command 0xE: PRNG[edit | edit source]

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

Command 0xF: Seed RNG buffer[edit | edit source]

This function seeds the Kirk 32-byte RNG buffer used to generate all the random data coming from Kirk.

It takes as an input and output data of size 0x1c:

  • 0x00 - 64-bit counter - increased by 1 in the output
  • 0x08 - seed data (0x14 bytes long) - used for seeding as an input, and contains fresh reseeded data for the output

Seeding works this way:

  1. Increment the input counter
  2. 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
  3. Initialize the 32-byte RNG buffer to two empty words, and two words taken from the input data at offsets 0x00 and 0x04
  4. Do a reseeding (see below)
  5. 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:

  1. Encrypt RNG buffer with AES per-console key with seed 6
  2. Set the last half of the PRNG seed (0x14 bytes) to the contents RNG buffer
  3. 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[edit | edit source]

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.

Input is:

  • 0x00: 0x20-byte long encrypted buffer containing the private key
  • 0x20: the message hash.

The output is a 0x28-byte long signature (r and s, both 0x14-byte long).

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.

Command 0x11: ECDSA signature verification[edit | edit source]

This command verifies an ECDSA signature. It is used to verify IdStorage IDPS certificates.

It takes no output, and takes as an input:

  • 0x00: public key
  • 0x28: signed message hash
  • 0x3C: signature r
  • 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).

Command 0x12: verify certificate[edit | edit source]

This command verifies an AES-CBC-MAC (OMAC1) signature. It is used to verify ID Storage certificates.

This command has no output.

It takes as input an ID Storage certificate read from IDStorage.

typedef struct kirk_command_0x12_input{
ids_cert_psp certificate;
} kirk_command_0x12_input;

It uses per-console key with seed 4.

Error codes[edit | edit source]

    0×00: Success
    0×01: Kirk not enabled
    0×02: Invalid mode
    0×03: Invalid header signature
    0×04: Invalid data signature
    0×05: Invalid ECDSA data
    0x0C: Kirk not seeded
    0x0D: Invalid operation (out of 1-18 range)
    0x0E: Invalid encryption keyseed
    0x0F: Invalid decryption keyseed
    0×10: Invalid data size (equals 0) (sign/cipher operations)

Code Samples[edit | edit source]

Open problems[edit | edit source]

  • 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.)
  • The Kirk's internal PRNG is deterministic but its function is unknown.
  • Elliptic curves have additional parameters specified in the code, which are unknown.