SLSK: Difference between revisions

From Vita Development Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 13: Line 13:
| 0x4 || 0x4 || Header Size || Offset to code. Usually 0x2C0.
| 0x4 || 0x4 || Header Size || Offset to code. Usually 0x2C0.
|-
|-
| 0x8 || 0x4 || Entrypoint Offset || Size of plaintext version string, 0 on FW 0.931, 0x10 on other.
| 0x8 || 0x4 || Version String Size || Size of Version String. 0 on FW 0.931, 0x10 on other.
|-
|-
| 0xC || 0x4 || Dynamic revoke block size || Size of dynamic key revoke block. Usually 0.
| 0xC || 0x4 || Dynamic revoke block size || Size of dynamic key revoke block. Usually 0.
Line 19: Line 19:
| 0x10 || 0x4 || Body Size || Code size. Usually 0xCE00.
| 0x10 || 0x4 || Body Size || Code size. Usually 0xCE00.
|-
|-
| 0x14 || 0x2 || AES Key Revision || Possible values are 0 to 5.
| 0x14 || 0x2 || Encryption Key Revision || AES-128-CBC Key revision. Possible values are 0 to 5.
|-
|-
| 0x16 || 0x2 || RSA Public Key Revision || Possible values are 0 to 15.
| 0x16 || 0x2 || Signature Public Key Revision || RSA Public Key Revision. Possible values are 0 to 15.
|-
|-
| 0x18 || 0x8 || Unknown || Usually zero.
| 0x18 || 0x8 || Unknown || Usually zeroes.
|-
|-
| 0x20 || 0x20 || Body Digest || SHA256 hash of decrypted body.
| 0x20 || 0x20 || Body Digest || SHA256 hash of decrypted body.
|-
|-
| 0x40 || 0x10 || ASCII Version || Version in ASCII. Not present on FW 0.931. Example: 0000360000000000.
| 0x40 || 0x10 || Version String || Version in ASCII. Not present on FW 0.931. Example: 0000360000000000.
|-
|-
| Variable (0x50 on FW 0.940-3.73, 0x40 on FW 0.931) || 0x90 || Static key revoke block || Allows any [[F00D Key Ring Base]] to be completely disabled.
| Variable (0x50 on FW 0.940-3.73, 0x40 on FW 0.931) || 0x90 || Static key revoke block || Allows any [[F00D Key Ring Base]] to be completely disabled.
Line 33: Line 33:
| Variable || Variable (usually 0) || Dynamic key revoke block || Allows specific permissions to be revoked from specified keys.
| Variable || Variable (usually 0) || Dynamic key revoke block || Allows specific permissions to be revoked from specified keys.
|-
|-
| Variable (0xE0 on FW 0.940-3.73, 0xD0 on FW 0.931) || 0xC0 || Metadata || 6 seeds of size 0x20. Contains keyslot fail keys up to certain point (FW <=3.69), non-fail keys afterwards (FW 3.70+).
| Variable (0xE0 on FW 0.940-3.73, 0xD0 on FW 0.931) || 0xC0 || Metadata || Personalized. 6 seeds of size 0x20. Contains keyslot fail keys up to certain point (FW <=3.69), non-fail keys afterwards (FW 3.70+).
|-
|-
| Variable (0x1A0 on FW 0.940-3.73, 0x190 on FW 0.931) || 0x20 || HMAC || HMAC-SHA256 of Header + Metadata.
| Variable (0x1A0 on FW 0.940-3.73, 0x190 on FW 0.931) || 0x20 || HMAC || Personalized. HMAC-SHA256 of Header + Metadata.
|-
|-
| Variable (0x1C0 on FW 0.940-3.73, 0x1B0 on FW 0.931) || 0x100 || Header Signature || Encrypted RSA signature of Header + Metadata + HMAC.
| Variable (0x1C0 on FW 0.940-3.73, 0x1B0 on FW 0.931) || 0x100 || Header Signature || Personalized. RSA signature of Header + Metadata + HMAC.
|-
|-
| Variable (0x2C0 on FW 0.940-3.73, 0x2B0 on FW 0.931) || Variable (usually 0xCE00) || Body || Encrypted body containing the payload.
| Variable (0x2C0 on FW 0.940-3.73, 0x2B0 on FW 0.931) || Variable (usually 0xCE00) || Body || Personalized. Executable code.
|-
|-
| Variable (usually 0xD0C0) || 0x100 || Footer Signature || RSA signature of Header + Body.
| Variable (usually 0xD0C0) || 0x100 || Footer Signature || RSA signature of Header + Metadata + HMAC + Body.
|-
|-
| Variable (usually 0xD1C0) || 0x140 || Random Padding || Must be filled to multiple of 0x200 bytes.
| Variable (usually 0xD1C0) || 0x140 || Random Padding || Must be filled to multiple of 0x200 bytes.
Line 48: Line 48:
=== Footer ===
=== Footer ===


The last 0x340 bytes of each SLSK is not personalized and not used in any way.
The last 0x340 bytes of each SLSK are not personalized and not used in any way.


== Bootrom ENC loading process ==
== Bootrom SLSK loading process ==


=== Secret debug mode ===
=== Secret debug mode ===


Before the ENC is loaded, there is a check for some secret mode. Note these two ports are used in regular syscon SPI-like communications. However, usually these two pins are used as part of the SPI-like protocol for signaling. But the bootrom does not use the SPI registers at all. It uses some registers that is never seen outside of bootrom. Even though it is logically separate from the SPI ports, it could be physically connected to the same pins although this is unconfirmed. Note that when the secret handshake passes and we are in secret mode, the MBR is read from the gamecard instead (with gamecard auth not enabled, so a regular SD card would work). Additionally, the personalization removal is done using keyslot 0x207 instead of 0x206 (see below) although it is not currently known if 0x207 is console-unique. All the signature checks and HMAC is still performed, so this secret mode cannot be used for running unsigned code. However, [[Glitching]] would still work when in the secret debug mode.
Before the SLSK is loaded, there is a check for some secret mode. Note these two ports are used in regular syscon SPI-like communications. However, usually these two pins are used as part of the SPI-like protocol for signaling. But the bootrom does not use the SPI registers at all. It uses some registers that are never seen outside of the bootrom. Even though it is logically separate from the SPI ports, it could be physically connected to the same pins although this is unconfirmed. Note that when the secret handshake passes and we are in secret mode, the MBR is read from the gamecard instead (with gamecard auth not enabled, so a regular SD card would work). Additionally, the personalization removal is done using keyslot 0x207 instead of 0x206 (see below) although it is not currently known if 0x207 is per-console. All the HMAC and signature checks are still performed, so this secret mode cannot be used to run unsigned code. However, [[Glitching]] would still work when in the secret debug mode.


<source lang="c">
<source lang="c">
Line 116: Line 116:
See also: [[Ernie_Firmware#Kermit_Bootrom_JIG_Mode]].
See also: [[Ernie_Firmware#Kermit_Bootrom_JIG_Mode]].


=== Remove personalization ===
=== Personalization Removal ===


First, personalization layer is removed. It uses AES-128-CBC with a derived key and decrypts data at ENC+0xE0 (or ENC+0xD0 if there's no plaintext version) for size of code_size+0x1E0.
First, personalization layer is removed. It uses AES-128-CBC with a derived key and decrypts data at Metadata offset (0xE0 on FW 0.940-3.73, 0xD0 on FW 0.931) for size of (body_size + 0xC0 (Metadata) + 0x20 (HMAC) + 0x100 (Header Signature)).


There are two possible paths to derive the key used to remove personalization. Normally, the key is derived using keyslot 0x206. There's however an alternative path, triggered in secret debug mode, when instead the keyslot 0x207 is used with a different seed.
There are two possible paths to derive the key used to remove personalization. Normally, the key is derived using keyslot 0x206. There is however an alternative path, triggered in secret debug mode, when instead keyslot 0x207 is used and with a different seed.


Once personalization is removed, the source keys are locked down. Keyslots 0x9, 0x206, 0x207 are locked down completely (leaving only 0xA0 protection). However, keyslot 0x8 allows encryption, this lets update manager SM add personalization layer during update without having to derive the keys itself.
Once personalization is removed, the source keyslots are locked down. Keyslots 0x9, 0x206, 0x207 are locked down completely (leaving only 0xA0 protection). However, keyslot 0x8 allows encryption, leaving Update Manager SM add personalization layer during firmware update without having to derive the keys itself.


=== Header RSA check ===
=== Header HMAC and RSA verification ===


A key is derived from keyslot 0x344 and put into keyslot 0x20. This key is then immediately used to calculate HMAC-SHA256 over enc header, excluding the RSA sig (typically 0x00 to 0x1C0).
A key is derived from keyslot 0x344 and put into keyslot 0x20. This key is then immediately used to calculate HMAC-SHA256 over SLSK header excluding the SLSK Header Signature (usually 0x00 to 0x1C0 or to 0x1B0).


2 bytes are read from keyring slot 0x603 is read. This is the bitmask of allowed RSA public keys (0xFFFF on 1.692). If the mask is zero, a hardcoded RSA modulus is used. Otherwise, it checks enc rsa revision against the mask and if it's allowed, it gets the modulus from keyring RSA storage starting at keyslot 0x700.
2 bytes are read from keyring slot 0x603. This is the bitmask of allowed RSA public keys (0xFFFF on 1.692). If the bitmask is zero, a hardcoded RSA modulus is used. Otherwise, it checks SLSK Signature Public Key Revision against the mask and if it is allowed, it gets the RSA modulus from keyring RSA storage starting at keyslot 0x700.


The signature is typically located at 0x1C0 and is 0x100 bytes. After calculating powmod, it checks the padding and compares previously calculated HMAC-SHA256 against the contents.
After calculating RSA powmod, it checks the padding over the SLSK Signature and compares previously calculated HMAC-SHA256 against the contents.


Finally, it protects keyslots 0x700 to 0x77F to disable reading out the modulus.
Finally, it protects keyslots 0x700 to 0x77F to disable reading out the modulus.


=== Metadata decryption and code verification ===
=== Metadata decryption and encrypted body verification ===


Using keyslot 0x208+aes_key_revision and metadata buffer (0xE0 offset for 0x20 bytes) the code decryption key is derived and put into keyslot 10. Then, 5 more keys are derived in the same way, using seed data [0x100; 0x19F]. These 5 keys are put into keyslots 11, 12, 13, 14, 15.
Using keyslot (0x208+enc_key_revision) and the SLSK Metadata first seed (0x20 bytes), the SLSK Body decryption key is derived and put into keyslot 0x10. Then, 5 more keys are derived in the same way, using SLSK Metadata seeds. These 5 keys are put into keyslots 0x11, 0x12, 0x13, 0x14 and 0w15.


Keyslots 0x208, 0x209, 0x20A, 0x20B, 0x20C, 0x20D (all possible AES key revision keys) are protected.
Once done, keyslots 0x208, 0x209, 0x20A, 0x20B, 0x20C, 0x20D (all possible keys bound to SLSK Encryption Key Revisions) are protected.


Data at [0x1A0; 0x1C0) is decrypted using keyslot 10. This is HMAC-SHA256 of the code segment. HMAC-SHA256 is calculated over the code segment using keyslot 0x20, then keyslot 0x20 is protected. Finally, the calculated hmac over the encrypted code is compared to the one decrypted in the header.
SLSK HMAC is decrypted using keyslot 0x10. This is HMAC-SHA256 of the encrypted Body segment. HMAC-SHA256 is calculated over the encrypted Body segment using keyslot 0x20, then keyslot 0x20 is protected. Finally, the calculated HMAC over the encrypted Body is compared to the HMAC decrypted from the SLSK header.


=== Protecting the keys ===
=== Protecting the keys ===


Some keys are protected, depending on bit flags buffer located right after plaintext version string (so, at offset 0x50). However, on the latest 3.68 enc it is all zeroes so no keys should be protected by this function (?)
Some keys are protected, depending on the bit flags buffer that we call Static key revoke block. However, on FW 3.68 SLSK, Static key revoke block is all zeroes so no keys should be protected by this function (?).


=== Decrypting code ===
=== Body decryption ===


Code is decrypted using key 10 and a hard coded IV. Then, the key is protected.
Body is decrypted using key 0x10 and a hardcoded IV. Then, the key is protected. The decrypted Body is the executable code segment.


=== Clear ===
One could guess that at this step, SHA256 of the decrypted Body is calculated and compared to the SLSK Header SHA256, but Team Molecule has not confirmed this theory.


The remainder (0x1C000 - code_sz) after the decrypted code is cleared with dmac. Dmac regs are also cleared.
=== Remainder area clearing ===
 
The remainder (0x1C000 - body_size) after the decrypted body is cleared with DMAC. DMAC registers are also cleared.

Revision as of 19:59, 23 December 2020

Location

SLSK files can be found in SLB2 as personalized and in SPKGs as non-personalized.

Structure

Offset Size Name Description
0x0 0x4 Magic Always 0x64B2C8E5.
0x4 0x4 Header Size Offset to code. Usually 0x2C0.
0x8 0x4 Version String Size Size of Version String. 0 on FW 0.931, 0x10 on other.
0xC 0x4 Dynamic revoke block size Size of dynamic key revoke block. Usually 0.
0x10 0x4 Body Size Code size. Usually 0xCE00.
0x14 0x2 Encryption Key Revision AES-128-CBC Key revision. Possible values are 0 to 5.
0x16 0x2 Signature Public Key Revision RSA Public Key Revision. Possible values are 0 to 15.
0x18 0x8 Unknown Usually zeroes.
0x20 0x20 Body Digest SHA256 hash of decrypted body.
0x40 0x10 Version String Version in ASCII. Not present on FW 0.931. Example: 0000360000000000.
Variable (0x50 on FW 0.940-3.73, 0x40 on FW 0.931) 0x90 Static key revoke block Allows any F00D Key Ring Base to be completely disabled.
Variable Variable (usually 0) Dynamic key revoke block Allows specific permissions to be revoked from specified keys.
Variable (0xE0 on FW 0.940-3.73, 0xD0 on FW 0.931) 0xC0 Metadata Personalized. 6 seeds of size 0x20. Contains keyslot fail keys up to certain point (FW <=3.69), non-fail keys afterwards (FW 3.70+).
Variable (0x1A0 on FW 0.940-3.73, 0x190 on FW 0.931) 0x20 HMAC Personalized. HMAC-SHA256 of Header + Metadata.
Variable (0x1C0 on FW 0.940-3.73, 0x1B0 on FW 0.931) 0x100 Header Signature Personalized. RSA signature of Header + Metadata + HMAC.
Variable (0x2C0 on FW 0.940-3.73, 0x2B0 on FW 0.931) Variable (usually 0xCE00) Body Personalized. Executable code.
Variable (usually 0xD0C0) 0x100 Footer Signature RSA signature of Header + Metadata + HMAC + Body.
Variable (usually 0xD1C0) 0x140 Random Padding Must be filled to multiple of 0x200 bytes.

Footer

The last 0x340 bytes of each SLSK are not personalized and not used in any way.

Bootrom SLSK loading process

Secret debug mode

Before the SLSK is loaded, there is a check for some secret mode. Note these two ports are used in regular syscon SPI-like communications. However, usually these two pins are used as part of the SPI-like protocol for signaling. But the bootrom does not use the SPI registers at all. It uses some registers that are never seen outside of the bootrom. Even though it is logically separate from the SPI ports, it could be physically connected to the same pins although this is unconfirmed. Note that when the secret handshake passes and we are in secret mode, the MBR is read from the gamecard instead (with gamecard auth not enabled, so a regular SD card would work). Additionally, the personalization removal is done using keyslot 0x207 instead of 0x206 (see below) although it is not currently known if 0x207 is per-console. All the HMAC and signature checks are still performed, so this secret mode cannot be used to run unsigned code. However, Glitching would still work when in the secret debug mode.

int is_debug_mode(void) {
    int res = 0;
    gpio_set_port_mode(0, 3, GPIO_MODE_OUT);
    if (gpio_port_read(0, 4)) {
        // this sets a bit in some f00d-only hardware
        // note this same reg is used to enable f00d reset from arm
        *(uint32_t *)0xE0020000 |= 0x10;
        // theory: mux on syscon SPI ports to connect to f00d directly

        // compute a challenge using true random numbers
        uint32_t challenge[4];
        challenge[0] = trng_read32();
        challenge[1] = trng_read32();
        challenge[2] = challenge[0];
        challenge[3] = challenge[1];

        // send challenge
        *(uint32_t *)0xE0000020 = challenge[0];
        *(uint32_t *)0xE0000024 = challenge[1];
        gpio_port_set(0, 3);

        // poll
        while (!gpio_port_read(0, 4));

        // get response
        uint32_t response[2];
        response[0] = *(uint32_t *)0xE0000028;
        response[1] = *(uint32_t *)0xE000002C;

        // clear regs
        *(uint32_t *)0xE0000028 = -1;
        *(uint32_t *)0xE000002C = -1;
        *(uint32_t *)0xE0000060 = -1; // maybe cached of 0xE0000020?
        *(uint32_t *)0xE0000064 = -1; // maybe cached of 0xE0000024?

        // end handshake
        gpio_port_clear(0, 3);

        // compute expected result
        uint32_t expected[4];
        if (bigmac_aes256_ecb_encrypt(expected, challenge, sizeof(challenge), g_debug_challenge_key) == 0) {
            // check result
            if (memcmp_timingsafe(expected, response, 8) == 0) {
                res = 1;
            }
        }
        memset(g_debug_challenge_key, 0, sizeof(g_debug_challenge_key));
        memset(challenge, 0, sizeof(challenge));
        memset(response, 0, sizeof(response));
        memset(expected, 0, sizeof(expected));
    } else {
        memset(g_debug_challenge_key, 0, sizeof(g_debug_challenge_key));
    }
    return res;
}

See also: Ernie_Firmware#Kermit_Bootrom_JIG_Mode.

Personalization Removal

First, personalization layer is removed. It uses AES-128-CBC with a derived key and decrypts data at Metadata offset (0xE0 on FW 0.940-3.73, 0xD0 on FW 0.931) for size of (body_size + 0xC0 (Metadata) + 0x20 (HMAC) + 0x100 (Header Signature)).

There are two possible paths to derive the key used to remove personalization. Normally, the key is derived using keyslot 0x206. There is however an alternative path, triggered in secret debug mode, when instead keyslot 0x207 is used and with a different seed.

Once personalization is removed, the source keyslots are locked down. Keyslots 0x9, 0x206, 0x207 are locked down completely (leaving only 0xA0 protection). However, keyslot 0x8 allows encryption, leaving Update Manager SM add personalization layer during firmware update without having to derive the keys itself.

Header HMAC and RSA verification

A key is derived from keyslot 0x344 and put into keyslot 0x20. This key is then immediately used to calculate HMAC-SHA256 over SLSK header excluding the SLSK Header Signature (usually 0x00 to 0x1C0 or to 0x1B0).

2 bytes are read from keyring slot 0x603. This is the bitmask of allowed RSA public keys (0xFFFF on 1.692). If the bitmask is zero, a hardcoded RSA modulus is used. Otherwise, it checks SLSK Signature Public Key Revision against the mask and if it is allowed, it gets the RSA modulus from keyring RSA storage starting at keyslot 0x700.

After calculating RSA powmod, it checks the padding over the SLSK Signature and compares previously calculated HMAC-SHA256 against the contents.

Finally, it protects keyslots 0x700 to 0x77F to disable reading out the modulus.

Metadata decryption and encrypted body verification

Using keyslot (0x208+enc_key_revision) and the SLSK Metadata first seed (0x20 bytes), the SLSK Body decryption key is derived and put into keyslot 0x10. Then, 5 more keys are derived in the same way, using SLSK Metadata seeds. These 5 keys are put into keyslots 0x11, 0x12, 0x13, 0x14 and 0w15.

Once done, keyslots 0x208, 0x209, 0x20A, 0x20B, 0x20C, 0x20D (all possible keys bound to SLSK Encryption Key Revisions) are protected.

SLSK HMAC is decrypted using keyslot 0x10. This is HMAC-SHA256 of the encrypted Body segment. HMAC-SHA256 is calculated over the encrypted Body segment using keyslot 0x20, then keyslot 0x20 is protected. Finally, the calculated HMAC over the encrypted Body is compared to the HMAC decrypted from the SLSK header.

Protecting the keys

Some keys are protected, depending on the bit flags buffer that we call Static key revoke block. However, on FW 3.68 SLSK, Static key revoke block is all zeroes so no keys should be protected by this function (?).

Body decryption

Body is decrypted using key 0x10 and a hardcoded IV. Then, the key is protected. The decrypted Body is the executable code segment.

One could guess that at this step, SHA256 of the decrypted Body is calculated and compared to the SLSK Header SHA256, but Team Molecule has not confirmed this theory.

Remainder area clearing

The remainder (0x1C000 - body_size) after the decrypted body is cleared with DMAC. DMAC registers are also cleared.