Kirk
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
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
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/10
This curve is used for the ECDSA verification of Kirk commands 1, 2, 3 and 10.
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
This curved is used for Kirk commands 12, 13, 14, 16 and 17.
p = 0xFFFFFFFFFFFFFFFEFFFFB5AE3C523E63944F2127 G = (0x128EC4256487FD8FDF64E2437BC0A1F6D5AFDE2C, 0x5958557EB1DB001260425524DBC379D5AC5F4ADF) n = 0xFFFFFFFFFFFFFFFF00000001FFFFFFFFFFFFFFFF a = -3 b = 0xA68BEDC33418029C1D3CE33B9A321FCCBB9E0F0B
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
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
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
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.
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 |
Per-console keys
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.
Seed | Usage |
---|---|
0 | Kirk commands 2 (encryption) & 3 (decryption) (the real encryption & CMAC keys are random, but this per-console key is used to encrypt them) |
1 | Kirk command 5 (encryption) & 8 (decryption) |
2 | Kirk command 6 (encryption) & 9 (decryption) |
3 | Kirk command 16 |
4 | Kirk command 18 |
5 | Unused |
6 | RNG buffer reseeding |
typedef struct ScePspKeyMesh { // size is 0x30
SceUInt8 aes128cbc_key_1[0x10]; // used by Kirk commands 5 & 8 and 16
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
} ScePspKeyMesh;
To generate the key mesh of a PSP, provided the Fuse ID (0xBC100090 and 0xBC100094 hardware registers), execute the following code.
void gen_psp_individual_seed() {
int i, k;
ScePspKeyMesh seed;
u8 subkey_1[0x10], subkey_2[0x10];
rijndael_ctx aes_ctx;
u8 fuseid[8];
// Byte-reverse the Fuse ID
u32 g_fuse90 = *(u32 *)0xBC100090;
u32 g_fuse94 = *(u32 *)0xBC100094;
fuseid[7] = g_fuse90 &0xFF;
fuseid[6] = (g_fuse90>>8) &0xFF;
fuseid[5] = (g_fuse90>>16) &0xFF;
fuseid[4] = (g_fuse90>>24) &0xFF;
fuseid[3] = g_fuse94 &0xFF;
fuseid[2] = (g_fuse94>>8) &0xFF;
fuseid[1] = (g_fuse94>>16) &0xFF;
fuseid[0] = (g_fuse94>>24) &0xFF;
rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key
for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID
subkey_2[i] = subkey_1[i] = fuseid[i % 8];
for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times
rijndael_encrypt(&aes_ctx, subkey_1, subkey_1);
rijndael_decrypt(&aes_ctx, subkey_2, subkey_2);
}
rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key
for (i = 0; i < 3; i++) { // encrypt 3, 6 and 9 times the subkey_2 to obtain the final keymesh
for (k = 0; k < 3; k++)
rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
memcpy(&seed[i * 0x10], subkey_2, 0x10);
}
}
The key mesh can then be used along with a seed to generate a key using the following algorithm:
void make_perconsole_key(u8 output[16], int seed, ScePspKeyMesh keymesh)
{
if (seed & 1) {
memcpy(output, keymesh.aes128cbc_key_2, 16);
} else {
memcpy(output, keymesh.aes128cbc_key_1, 16);
}
// Encrypt the result several times depending on the seed
rijndael_set_key(&aes_ctx, keymesh.aes128cbc_derivation_key);
seed = (seed / 2) + 1;
while ((seed--) >= 0) {
rijndael_encrypt(&aes_ctx, output);
}
}
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 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
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 0: decrypt kbooti
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.
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 1, 2, 3 & 10: decryption and authentication
Overview
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 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).
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
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
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
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
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
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.
- 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.
Command 2
Command 2 is a bit more complicated as it re-encrypts data for command 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).
Command 10
Its behavior is very simple:
- 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
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
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 11: 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.
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 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 13: ECDSA point multiplication
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 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.)
Command 15: Seed RNG buffer
This function seeds the Kirk RNG buffer used to generate all the random data coming from Kirk.
It takes as an input and output data of size 0x1c:
- 0x00 - unknown - modified by an unknown opcode
- 0x04 - counter - increased by 1 in the output
- 0x08 - seed data - used for seeding, and contains fresh reseeded data for the output
Seeding works this way:
- Increase input counter and do unknown operation on offset 0x00
- Set the PRNG seed to the input seed data, XOR'ed with a SHA1 of data coming from a true random number generator
- Initialize RNG buffer to two empty words, and then output data at offsets 0x00 and 0x04
- Do a reseeding
- Output resulting buffer.
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
- Reseed the PRNG with the RNG buffer
- Regenerate data with the PRNG
Command 16: 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:
- 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 17: ECDSA signature verification
This command verifies an ECDSA signature.
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 18: verify certificate
This command has no output.
It takes as an input a 0xB8-long buffer:
- 0x00: certificate data (either ConsoleID or OpenPSID) and ECDSA signature etc. (unused here)
- 0xA8: AES-CMAC hash of the rest of the header.
It verifies the AES CMAC of the header using per-console key with seed 4.
Error codes
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
Open problems
- 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.