Difference between revisions of "Cryptosystem"

From Nintendo Switch Brew
Jump to navigation Jump to search
(Thanks to SciresM for helping with writing this page)
 
(48 intermediate revisions by 12 users not shown)
Line 1: Line 1:
Like the 3DS, the Switch relies on a number of cryptographic keys to prevent unauthorized persons from dumping and analyzing its software and assets. This page will focus on the "symmetric" cryptography involved in the Switch's cryptosystem.
+
= BootROM =
 +
The bootrom initializes two keyslots in the hardware engine:
  
== BootROM ==
+
* the SBK (Secure Boot Key) in keyslot 14
 +
* the SSK (Secure Storage Key) in keyslot 15.
  
The Switch's BootROM does no symmetric cryptographic operations. However, it sets up two keys in the hardware security engine's keyslots: the SBK (Secure Boot Key) in keyslot 0xE and the SSK (Secure Storage Key) in keyslot 0xF. Reads from both of these keyslots are disabled by the bootROM. The material used to generate these keys is stored in special fuses that have their access disabled by the bootROM.
+
Reads from both of these keyslots are disabled by the bootROM.
 +
The SBK is stored in [[Fuses#FUSE_PRIVATE_KEY|FUSE_PRIVATE_KEY]], which are locked to read out only FFs after the bootrom finishes.
  
The SBK is common to all consoles while the SSK is console unique. The SSK is not used on retail devices.
+
SBK is '''unique''' per console, and not shared among consoles as originally believed.
== Falcon coprocessor ==
 
  
The falcon processor (TSEC) stores a special console-unique key (that will be referred to as the "device keyblob seed generation key") in fuses that only microcode authenticated by NVidia has access to.
+
The SSK is derived on boot via the SBK, the 32-bit console-unique "Device Key", and hardware information stored in fuses.
  
== Bootloader stage 1 ==
+
Pseudocode for the derivation is as follows:
 +
  void generateSSK() {
 +
      char keyBuffer[0x10]; // Used to store keydata
 +
      uint hwInfoBuffer[4]; // Used to store info about hardware from fuses
 +
      uint deviceKey = getDeviceKey(); // Reads 32-bit device key from FUSE_PRIVATE_KEY4.
 +
      for (int i = 0; i < 4; i++) { // Keybuffer = deviceKey || deviceKey || deviceKey || deviceKey
 +
          ((uint *)keyBuffer)[i] = deviceKey;
 +
      }
 +
     
 +
      encryptWithSBK(keyBuffer); // keyBuffer = AES-ECB(SBK, deviceKey || {...})
 +
     
 +
      // Set up Hardware info buffer
 +
      uint vendor_code = *((uint *)0x7000FA00) & 0x0000000F; // FUSE_VENDOR_CODE
 +
      uint fab_code    = *((uint *)0x7000FA04) & 0x0000003F; // FUSE_FAB_CODE
 +
      uint lot_code_0  = *((uint *)0x7000FA08) & 0xFFFFFFFF; // FUSE_LOT_CODE_0
 +
      uint lot_code_1  = *((uint *)0x7000FA0C) & 0x0FFFFFFF; // FUSE_LOT_CODE_1
 +
      uint wafer_id    = *((uint *)0x7000FA10) & 0x0000003F; // FUSE_WAFER_ID
 +
      uint x_coord    = *((uint *)0x7000FA14) & 0x000001FF; // FUSE_X_COORDINATE
 +
      uint y_coord    = *((uint *)0x7000FA18) & 0x000001FF; // FUSE_Y_COORDINATE
 +
      uint unk_hw_fuse = *((uint *)0x7000FA20) & 0x0000003F; // Unknown cached fuse.
 +
     
 +
      // HARDWARE_INFO_BUFFER = unk_hw_fuse || Y_COORD || X_COORD || WAFER_ID || LOT_CODE || FAB_CODE || VENDOR_ID
 +
      hwInfoBuffer[0] = (lot_code_1 << 30) | (wafer_id << 24) | (x_coord << 15) | (y_coord << 6) | unk_hw_fuse;
 +
      hwInfoBuffer[1] = (lot_code_0 << 26) | (lot_code_1 >> 2);
 +
      hwInfoBuffer[2] = (fab_code << 26) | (lot_code_0 >> 6);
 +
      hwInfoBuffer[3] = vendor_code;
 +
     
 +
      for (int i = 0; i < 0x10; i++) { // keyBuffer = XOR(AES-ECB(SBK, deviceKey || {...}), HARDWARE_INFO_BUFFER)
 +
          keyBuffer[i] ^= ((char *)hwInfoBuffer)[i];
 +
      }
 +
     
 +
      encryptWithSBK(keyBuffer); // keyBuffer = AES-ECB(SBK, XOR(AES-ECB(SBK, deviceKey || {...}), HARDWARE_INFO_BUFFER))
 +
     
 +
      setKeyslot(KEYSLOT_SSK, keyBuffer); // SSK = keyBuffer.
 +
  }
 +
 +
= Falcon coprocessor =
 +
The falcon processor (TSEC) generates a special console-unique key (that will be referred to as the "tsec key").
  
=== Key generation ===
+
This is presumably using data stored in fuses that only microcode authenticated by NVidia has access to.
  
Bootloader stage 1 generates three keys: the stage 2 decryption key, stored in keyslot 0xB; the master static key, which will be used by stage 2 to generate all the static keys, stored in keyslot 0xC; and the master device key, which will be used by stage 2 to generate all the device-specific keys, stored in keyslot 0xD.
+
= Package1ldr =
Keyslot 0xB is cleared after it is used to decrypt the stage 2 bootloader; only keyslots 0xC and 0xD will be transferred to stage 2. Additionally, keyslots 0xC and 0xD are set read-only after they are generated. The SBK and the SSK are also cleared after use (although the SSK isn't used at all, except on dev units).
+
== Key table ==
 +
[1.0.0-3.0.2] During package1ldr:
 +
{| class="wikitable" border="1"
 +
|-
 +
! Keyslot
 +
! Name
 +
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 11
 +
| Package1Key
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes
 +
|-
 +
| 14
 +
| SecureBootKey
 +
| Bootrom
 +
| Yes
 +
| No
 +
|-
 +
| 15
 +
| SecureStorageKey
 +
| Bootrom
 +
| Yes
 +
| No
 +
|}
  
The master static key is generated by decrypting the master static seed (a constant stored in bootloader .data) with the master static key encryption key. The master static seed used varies depending on whether the console is a retail unit or a dev unit.
+
[1.0.0-3.0.2] After package1ldr:
Both the master static key encryption key and the stage 2 key are stored in a keyblob. The following table describes the keyblob format.
+
{| class="wikitable" border="1"
 +
|-
 +
! Keyslot
 +
! Name
 +
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 +
| MasterKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes, on security updates
 +
|-
 +
| 13
 +
| PerConsoleKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 +
|}
  
 +
[4.0.0+] After package1ldr (Secure Monitor boot):
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Offset
+
! Keyslot
! Size
+
! Name
! Description
+
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 +
| MasterKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes, on security updates
 
|-
 
|-
| 0x0
+
| 13
| 0x10
+
| PerConsoleKeyForFirmwareSpecificPerConsoleKeyGen
| AES-CMAC over the next 0xA0 bytes
+
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 
|-
 
|-
| 0x10
+
| 14
| 0x10
+
| StaticKeyForFirmwareSpecificPerConsoleKeyGen
| CTR
+
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes, on security updates
 
|-
 
|-
| 0x20
+
| 15
| 0x90
+
| PerConsoleKey
| Encrypted keydata
+
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 
|}
 
|}
  
Decrypted Keydata format:
+
[4.0.0+] After package1ldr (Secure Monitor runtime):
 +
{| class="wikitable" border="1"
 +
|-
 +
! Keyslot
 +
! Name
 +
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 +
| MasterKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes, on security updates
 +
|-
 +
| 13
 +
| FirmwareSpecificPerConsoleKey
 +
| Secure Monitor init
 +
| Yes
 +
| Yes, on security updates
 +
|-
 +
| 15
 +
| PerConsoleKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 +
|}
  
 +
[6.2.0+] After package1ldr/TSEC Payload (Secure Monitor boot):
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Offset
+
! Keyslot
! Size
+
! Name
! Description
+
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 +
| TsecKey
 +
| [[TSEC#Payload|Package1ldr TSEC Firmware]]
 +
| Yes
 +
| No
 +
|-
 +
| 13
 +
| TsecRootKey
 +
| [[TSEC#Payload|Package1ldr TSEC Firmware]]
 +
| No
 +
| Unknown
 
|-
 
|-
| 0x0
+
| 14
| 0x80
+
| SecureBootKey
| Array of master static key encryption keys
+
| Bootrom
 +
| Yes
 +
| No
 
|-
 
|-
| 0x80
+
| 15
| 0x10
+
| SecureStorageKey
| Stage 2 key
+
| Bootrom
 +
| Yes
 +
| No
 
|}
 
|}
  
32 of these blobs are stored in the eMMC. Only one at a time is loaded, it is controlled by the bootloader version field in the BCT (at +0x2330).
+
== Key generation ==
 +
Note: aes_unwrap(wrapped_key, wrap_key) is just another name for a single AES-128 block decryption.
 +
 
 +
If bit0 of 0x7000FB94 is clear, it will initialize keys like this (probably used for internal development units only):
 +
  // Final keys:
 +
  package1_key    /* slot11 */ = aes_unwrap(f5b1eadb.., sbk)
 +
  master_key      /* slot12 */ = aes_unwrap(bct->pubkey[0] == 0x11 ? simpleseed_dev0 : simpleseed_dev1, aes_unwrap(5ff9c2d9.., sbk))
 +
  per_console_key /* slot13 */ = aes_unwrap(4f025f0e..., aes_unwrap(6e4a9592.., ssk))
 +
 
 +
[4.0.0+] Above method was removed.
 +
 
 +
Normal key generation looks like this on 1.0.0/2.0.0:
  
Although the keydata is presumably common to all consoles, each keyblob is console-unique, because the key used to encrypt it is at the factory is console unique. Each keyblob has its own encryption key, with keyblob key N generated by decrypting keyblob key seed N with the SBK, and keyblob key seed N generated by decrypting keyblob N's seed constant with the device keyblob seed generation key obtained from the Falcon. Keyblob key 1 is special: In addition to being used to decrypt keyblob 1, it is also used to generate the master device key by decrypting a constant block.
+
  keyblob_key /* slot13 */ = aes_unwrap(aes_unwrap(wrapped_keyblob_key, tsec_key /* slot13 */), sbk /* slot14 */)
 +
  cmac_key    /* slot11 */ = aes_unwrap(59c7fb6f.., keyblob_key)
 +
 
 +
  if aes_cmac(buf=keyblob+0x10, len=0xA0, cmac_key) != keyblob[0:0x10]:
 +
    panic()
 +
 
 +
  aes_ctr_decrypt(buf=keyblob+0x20, len=0x90, iv=keyblob+0x10 key=keyblob_key)
 +
 
 +
  // Final keys:
 +
  package1_key    /* slot11 */ = keyblob[0x80:0x90]
 +
  master_key      /* slot12 */ = aes_unwrap(bct->pubkey[0] == 0x4f ? normalseed_dev : normalseed_retail, keyblob+0x20)
 +
  per_console_key /* slot13 */ = aes_unwrap(4f025f0e.., keyblob_key)
 +
 
 +
.. and on 3.0.0, they moved keyslots around a little to generate the same per-console key as 1.0.0:
 +
 
 +
  old_keyblob_key /* slot10 */ = aes_unwrap(aes_unwrap(df206f59.., tsec_key /* slot13 */), sbk /* slot14 */)
 +
  keyblob_key    /* slot13 */ = aes_unwrap(aes_unwrap(wrapped_keyblob_key, tsec_key /* slot13 */), sbk /* slot14 */)
 +
  cmac_key        /* slot11 */ = aes_unwrap(59c7fb6f.., keyblob_key)
 +
 
 +
  if aes_cmac(buf=keyblob+0x10, len=0xA0, cmac_key) != keyblob[0:0x10]:
 +
    panic()
 +
 
 +
  aes_ctr_decrypt(buf=keyblob+0x20, len=0x90, iv=keyblob+0x10 key=keyblob_key)
 +
 
 +
  // Final keys:
 +
  package1_key    /* slot11 */ = keyblob[0x80:0x90]
 +
  master_key      /* slot12 */ = aes_unwrap(bct->pubkey[0] == 0x4f ? normalseed_dev : normalseed_retail, keyblob+0x20)
 +
  per_console_key /* slot13 */ = aes_unwrap(4f025f0e.., old_keyblob_key)
 +
 
 +
.. and on 4.0.0 it was further moved around:
 +
 
 +
  keyblob_key    /* slot13 */ = aes_unwrap(aes_unwrap(wrapped_keyblob_key, tsec_key /* slot13 */), sbk /* slot14 */)
 +
  cmac_key        /* slot11 */ = aes_unwrap(59c7fb6f.., keyblob_key)
 +
 
 +
  if aes_cmac(buf=keyblob+0x10, len=0xA0, cmac_key) != keyblob[0:0x10]:
 +
    panic()
 +
 
 +
  aes_ctr_decrypt(buf=keyblob+0x20, len=0x90, iv=keyblob+0x10 key=keyblob_key)
 +
 
 +
  // Final keys:
 +
  package1_key        /* slot11 */ = keyblob[0x80:0x90]
 +
  master_key          /* slot12 */ = aes_unwrap(normalseed_retail, keyblob+0x20)
 +
  new_master_key      /* slot14 */ = aes_unwrap(2dc1f48d.., keyblob+0x20)
 +
  new_per_console_key /* slot13 */ = aes_unwrap(0c9109db.., old_keyblob_key)
 +
  per_console_key    /* slot15 */ = aes_unwrap(4f025f0e.., old_keyblob_key)
 +
 
 +
.. and on 6.2.0, they moved key generation out of package1ldr, and into the Secure Monitor's boot section:
 +
 
 +
  clear_keyslots_other_than_12_13_and_14()
 +
 
 +
  old_keyblob_key /* slot15 */ = aes_unwrap(aes_unwrap(df206f59.., tsec_key /* slot12 */), sbk /* slot14 */)
 +
  /* Previously, master_kek was stored at keyblob+0x20) */
 +
  master_kek      /* slot13 */ = aes_unwrap(374b7729.. /* probably firmware specific */, tsec_root_key /* slot13 */)
 +
 
 +
  clear_keyslot(12)
 +
 
 +
  // Final keys:
 +
  new_master_key      /* slot12 */ = aes_unwrap(2dc1f48d.., master_kek)       
 +
  master_key          /* slot13 */ = aes_unwrap(normalseed_retail, master_kek)
 +
  new_per_console_key /* slot14 */ = aes_unwrap(0c9109db.., old_keyblob_key)
 +
  per_console_key    /* slot15 */ = aes_unwrap(4f025f0e.., old_keyblob_key)
 +
 
 +
 
 +
SBK and SSK keyslots are cleared after keys have been generated.
 +
 
 +
See table above for which keys are console unique.
  
 
The key used to verify a keyblob's MAC is not the keyblob key but a key derived from it; this is likely part of an attempt to mitigate side-channel attacks as the MAC is an alterable part of the keyblob.
 
The key used to verify a keyblob's MAC is not the keyblob key but a key derived from it; this is likely part of an attempt to mitigate side-channel attacks as the MAC is an alterable part of the keyblob.
  
The bootloader only stores the seed constants for the keyblob loaded by the current revision and for keyblob 1 (So that the master device key can be generated).  
+
The bootloader only stores the hardcoded constants for the keyblob used in the current revision. Nintendo are withholding all the future hardcoded constants.
  
This mechanism provides several advantages. If the stage 2 bootloader is compromised, stage 1 can just use another master static key in the keyblob. If stage 1 itself is glitched or exploited in such a way the keyblob is dumped, Nintendo just has to change the loaded keyblob: the vulnerable bootloader won't be able to decrypt the new keyblob, as the keyblob key it knows is different from the one needed. Even if somehow an exploit or glitch allowed one to be able to use the SBK to generate keyblob keys, the seed constants for future keyblobs are unknown (and will be until Nintendo releases new bootloaders that use them), and so the exploit or glitch would have to be re-done on each new bootloader revision (if it's not patched).
+
This means that if you have an attack on the bootloader, you need to re-preform it every time they move to a new keyblob.
  
==== Summary of key derivations ====
+
Dumping the SBK and TSEC key of any single system should be enough to derive all key material on the system, prior to 6.2.0.
  
The device keyblob seed generation key is unique to each device and obtained from the Falcon. It is used to generate one or several of the keyblob key seeds by decrypting keybloob's seed constants. The keyblob key seeds are decrypted by the SBK to create the keyblob keys.
+
The key-derivation is described in more detail [[Package1#Key_generation|here]].
Each keyblob key is used to decrypt its associated keyblob's keydata, and the keyblob key for the first keyblob is additionally used to generate the master device key.
 
Each keyblob stores 8 master static key encryption keys, and the stage 2 bootloader decryption key. The master static key is generated by decrypting the master static key seed (one of two constants depending on retail or dev unit) with the master static key encryption key.
 
  
=== Step by step generation code ===
+
=== Keyblob ===
 +
There are 32 keyblobs written to NAND at factory, with each keyblob encrypted with a console-unique key derived from the console's SBK, the console's tsec key, and a constant specific to each keyblob.
  
* Falcon microcode is loaded, the device keyblob seed generation key is obtained from the Falcon.
+
Despite being encrypted with console unique keys, though, the decrypted keyblob contents are shared for all consoles.
* The device keyblob seed generation key is stored in keyslot 0xD.
 
* [3.0.0+] keyblob key seed 1 is generated by decrypting the keyblob seed constant 1 with the device keyblob seed generation key
 
* [3.0.0+] keyblob key 1 is generated by decrypting keyblob key seed 1 with the SBK. The result is directly stored in keyslot 0xA without leaving the crypto engine.
 
* keyblob key seed N is generated by decrypting the keyblob seed constant N with the device keyblob seed generation key
 
* keyblob key N is generated by decrypting keyblob key seed N with the SBK. The result is directly stored in keyslot 0xD without leaving the crypto engine.
 
* The SBK and the SSK are cleared.
 
* The constant MAC key generator block is decrypted with keyblob key N to generate keyblob MAC key N. The result is directly stored in keyslot 0xB without leaving the crypto engine.
 
* With keyblob MAC key N, AES CMAC is performed over the keyblob.
 
* With a comparison function which is safe against timing attacks, the CMAC is compared with the stored CMAC. If they differ, panic is called.
 
* The keyblob data is decrypted with AES-CTR, using the keyblob key N and the stored CTR.
 
* The stage 2 decryption key (the ninth key in the blob) is loaded in keyslot 0xB.
 
* The master static key encryption key. is loaded in keyslot 0xC.
 
* The decrypted keyblob data is erased.
 
* The master static key is generated by decrypting the master static seed with the master static key encryption key. The result is directly stored in keyslot 0xC without leaving the crypto engine.
 
* [1.0.0-2.3.0] The master device key is generated by decrypting a constant block with keyslot 0xD (which contains keyblob N's key 1). The result is directly stored in keyslot 0xD without leaving the crypto engine.
 
* [3.0.0+] The master device key is generated by decrypting a constant block with keyslot 0xA (which contains keyblob 1's key 1). The result is directly stored in keyslot 0xD without leaving the crypto engine.
 
* [3.0.0+] Keyslot 0xA is cleared.
 
  
==== Table of used keyblobs ====
+
Used keyblobs are as follows:
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
Line 107: Line 310:
 
| 3.0.0
 
| 3.0.0
 
| 2
 
| 2
 +
| 1
 +
|-
 +
| 3.0.1-3.0.2
 +
| 3
 +
| 1
 +
|-
 +
| 4.0.0-4.1.0
 +
| 4
 +
| 1
 +
|-
 +
| 5.0.0-5.1.0
 +
| 5
 +
| 1
 +
|-
 +
| 6.0.0-6.1.0
 +
| 6
 
| 1
 
| 1
 
|}
 
|}
  
== Bootloader stage 2 ==
+
Starting from 6.2.0, key generation no longer uses keyblobs.
 +
 
 +
=== Seeds ===
 +
  normalseed_retail = d8a2410a...
 +
 
 +
  [1.0.0] wrapped_keyblob_key = df206f59...
 +
  [1.0.0] simpleseed_dev0  = aff11423...
 +
  [1.0.0] simpleseed_dev1  = 5e177ee1...
 +
  [1.0.0] normalseed_dev    = 0542a0fd...
 +
 
 +
  [3.0.0] wrapped_keyblob_key = 0c25615d... 
 +
  [3.0.0] simpleseed_dev0  = de00216a...
 +
  [3.0.0] simpleseed_dev1  = 2db7c0a1...
 +
  [3.0.0] normalseed_dev    = 678c5a03...
 +
 
 +
  [3.0.1] wrapped_keyblob_key = 337685ee... 
 +
  [3.0.1] simpleseed_dev0  = e045f5ba...
 +
  [3.0.1] simpleseed_dev1  = 84d92e0d...
 +
  [3.0.1] normalseed_dev    = cd88155b...
 +
 
 +
  [4.0.0] wrapped_keyblob_key = 2d1f4880...
 +
 
 +
=== Versions ===
 +
The key generation system has historically been revised several times. Each version is bound to a specific BCT public key and can be identified by its first byte as follows:
  
It is currently unknown what key generation the stage 2 bootloader does.
+
{| class="wikitable" border="1"
 +
|-
 +
! Version
 +
! BCT public key's first byte
 +
! Description
 +
|-
 +
| K1
 +
| 0x11
 +
| Erista prototype development
 +
|-
 +
| K2
 +
| 0xFB
 +
| Erista prototype development
 +
|-
 +
| K3
 +
| 0x4F
 +
| Erista prototype development
 +
|-
 +
| K4
 +
|
 +
| Erista prototype retail
 +
|-
 +
| K5
 +
| 0x37
 +
| Erista development
 +
|-
 +
| K6
 +
| 0xF7
 +
| Erista retail
 +
|-
 +
| M1
 +
| 0xDD
 +
| Mariko prototype development
 +
|-
 +
| M2
 +
| 0xC3
 +
| Mariko development
 +
|-
 +
| M3
 +
| 0x9B
 +
| Mariko retail
 +
|}
  
== Secure world (TrustZone) software ==
+
= Secure Monitor Init =
 +
On all versions, the key to decrypt [[Package2]] is generated by decrypting a constant seed with the master key. The key is erased after use. 
  
The Secure world software performs all runtime cryptographic operations.
+
Additionally, starting from 4.0.0, the Secure Monitor init will decrypt another constant seed successively with a special per console key and a special static key passed by package1loader, to generate the firmware specific per-console key. The operation will erase these special keys passed by package1loader.  
  
It is currently unknown what operations the Secure world software performs.
+
= Secure Monitor =
 +
The secure monitor performs some runtime cryptographic operations. See [[SMC]] for what operations it provides.

Revision as of 19:53, 11 April 2020

BootROM

The bootrom initializes two keyslots in the hardware engine:

  • the SBK (Secure Boot Key) in keyslot 14
  • the SSK (Secure Storage Key) in keyslot 15.

Reads from both of these keyslots are disabled by the bootROM. The SBK is stored in FUSE_PRIVATE_KEY, which are locked to read out only FFs after the bootrom finishes.

SBK is unique per console, and not shared among consoles as originally believed.

The SSK is derived on boot via the SBK, the 32-bit console-unique "Device Key", and hardware information stored in fuses.

Pseudocode for the derivation is as follows:

 void generateSSK() {
     char keyBuffer[0x10]; // Used to store keydata
     uint hwInfoBuffer[4]; // Used to store info about hardware from fuses
     uint deviceKey = getDeviceKey(); // Reads 32-bit device key from FUSE_PRIVATE_KEY4.
     for (int i = 0; i < 4; i++) { // Keybuffer = deviceKey || deviceKey || deviceKey || deviceKey
         ((uint *)keyBuffer)[i] = deviceKey;
     }
     
     encryptWithSBK(keyBuffer); // keyBuffer = AES-ECB(SBK, deviceKey || {...})
     
     // Set up Hardware info buffer
     uint vendor_code = *((uint *)0x7000FA00) & 0x0000000F; // FUSE_VENDOR_CODE
     uint fab_code    = *((uint *)0x7000FA04) & 0x0000003F; // FUSE_FAB_CODE
     uint lot_code_0  = *((uint *)0x7000FA08) & 0xFFFFFFFF; // FUSE_LOT_CODE_0
     uint lot_code_1  = *((uint *)0x7000FA0C) & 0x0FFFFFFF; // FUSE_LOT_CODE_1
     uint wafer_id    = *((uint *)0x7000FA10) & 0x0000003F; // FUSE_WAFER_ID
     uint x_coord     = *((uint *)0x7000FA14) & 0x000001FF; // FUSE_X_COORDINATE
     uint y_coord     = *((uint *)0x7000FA18) & 0x000001FF; // FUSE_Y_COORDINATE
     uint unk_hw_fuse = *((uint *)0x7000FA20) & 0x0000003F; // Unknown cached fuse.
     
     // HARDWARE_INFO_BUFFER = unk_hw_fuse || Y_COORD || X_COORD || WAFER_ID || LOT_CODE || FAB_CODE || VENDOR_ID
     hwInfoBuffer[0] = (lot_code_1 << 30) | (wafer_id << 24) | (x_coord << 15) | (y_coord << 6) | unk_hw_fuse;
     hwInfoBuffer[1] = (lot_code_0 << 26) | (lot_code_1 >> 2);
     hwInfoBuffer[2] = (fab_code << 26) | (lot_code_0 >> 6);
     hwInfoBuffer[3] = vendor_code;
     
     for (int i = 0; i < 0x10; i++) { // keyBuffer = XOR(AES-ECB(SBK, deviceKey || {...}), HARDWARE_INFO_BUFFER)
         keyBuffer[i] ^= ((char *)hwInfoBuffer)[i];
     }
     
     encryptWithSBK(keyBuffer); // keyBuffer = AES-ECB(SBK, XOR(AES-ECB(SBK, deviceKey || {...}), HARDWARE_INFO_BUFFER))
     
     setKeyslot(KEYSLOT_SSK, keyBuffer); // SSK = keyBuffer.
 }

Falcon coprocessor

The falcon processor (TSEC) generates a special console-unique key (that will be referred to as the "tsec key").

This is presumably using data stored in fuses that only microcode authenticated by NVidia has access to.

Package1ldr

Key table

[1.0.0-3.0.2] During package1ldr:

Keyslot Name Set by Per-console Per-firmware
11 Package1Key Package1ldr No Yes
14 SecureBootKey Bootrom Yes No
15 SecureStorageKey Bootrom Yes No

[1.0.0-3.0.2] After package1ldr:

Keyslot Name Set by Per-console Per-firmware
12 MasterKey Package1ldr No Yes, on security updates
13 PerConsoleKey Package1ldr Yes No

[4.0.0+] After package1ldr (Secure Monitor boot):

Keyslot Name Set by Per-console Per-firmware
12 MasterKey Package1ldr No Yes, on security updates
13 PerConsoleKeyForFirmwareSpecificPerConsoleKeyGen Package1ldr Yes No
14 StaticKeyForFirmwareSpecificPerConsoleKeyGen Package1ldr No Yes, on security updates
15 PerConsoleKey Package1ldr Yes No

[4.0.0+] After package1ldr (Secure Monitor runtime):

Keyslot Name Set by Per-console Per-firmware
12 MasterKey Package1ldr No Yes, on security updates
13 FirmwareSpecificPerConsoleKey Secure Monitor init Yes Yes, on security updates
15 PerConsoleKey Package1ldr Yes No

[6.2.0+] After package1ldr/TSEC Payload (Secure Monitor boot):

Keyslot Name Set by Per-console Per-firmware
12 TsecKey Package1ldr TSEC Firmware Yes No
13 TsecRootKey Package1ldr TSEC Firmware No Unknown
14 SecureBootKey Bootrom Yes No
15 SecureStorageKey Bootrom Yes No

Key generation

Note: aes_unwrap(wrapped_key, wrap_key) is just another name for a single AES-128 block decryption.

If bit0 of 0x7000FB94 is clear, it will initialize keys like this (probably used for internal development units only):

 // Final keys:
 package1_key    /* slot11 */ = aes_unwrap(f5b1eadb.., sbk)
 master_key      /* slot12 */ = aes_unwrap(bct->pubkey[0] == 0x11 ? simpleseed_dev0 : simpleseed_dev1, aes_unwrap(5ff9c2d9.., sbk))
 per_console_key /* slot13 */ = aes_unwrap(4f025f0e..., aes_unwrap(6e4a9592.., ssk))

[4.0.0+] Above method was removed.

Normal key generation looks like this on 1.0.0/2.0.0:

 keyblob_key /* slot13 */ = aes_unwrap(aes_unwrap(wrapped_keyblob_key, tsec_key /* slot13 */), sbk /* slot14 */)
 cmac_key    /* slot11 */ = aes_unwrap(59c7fb6f.., keyblob_key)
 
 if aes_cmac(buf=keyblob+0x10, len=0xA0, cmac_key) != keyblob[0:0x10]:
   panic()
 
 aes_ctr_decrypt(buf=keyblob+0x20, len=0x90, iv=keyblob+0x10 key=keyblob_key)
 
 // Final keys:
 package1_key    /* slot11 */ = keyblob[0x80:0x90]
 master_key      /* slot12 */ = aes_unwrap(bct->pubkey[0] == 0x4f ? normalseed_dev : normalseed_retail, keyblob+0x20)
 per_console_key /* slot13 */ = aes_unwrap(4f025f0e.., keyblob_key)

.. and on 3.0.0, they moved keyslots around a little to generate the same per-console key as 1.0.0:

 old_keyblob_key /* slot10 */ = aes_unwrap(aes_unwrap(df206f59.., tsec_key /* slot13 */), sbk /* slot14 */)
 keyblob_key     /* slot13 */ = aes_unwrap(aes_unwrap(wrapped_keyblob_key, tsec_key /* slot13 */), sbk /* slot14 */)
 cmac_key        /* slot11 */ = aes_unwrap(59c7fb6f.., keyblob_key)
 
 if aes_cmac(buf=keyblob+0x10, len=0xA0, cmac_key) != keyblob[0:0x10]:
   panic()
 
 aes_ctr_decrypt(buf=keyblob+0x20, len=0x90, iv=keyblob+0x10 key=keyblob_key)
 
 // Final keys:
 package1_key    /* slot11 */ = keyblob[0x80:0x90]
 master_key      /* slot12 */ = aes_unwrap(bct->pubkey[0] == 0x4f ? normalseed_dev : normalseed_retail, keyblob+0x20)
 per_console_key /* slot13 */ = aes_unwrap(4f025f0e.., old_keyblob_key)

.. and on 4.0.0 it was further moved around:

 keyblob_key     /* slot13 */ = aes_unwrap(aes_unwrap(wrapped_keyblob_key, tsec_key /* slot13 */), sbk /* slot14 */)
 cmac_key        /* slot11 */ = aes_unwrap(59c7fb6f.., keyblob_key)
 
 if aes_cmac(buf=keyblob+0x10, len=0xA0, cmac_key) != keyblob[0:0x10]:
   panic()
 
 aes_ctr_decrypt(buf=keyblob+0x20, len=0x90, iv=keyblob+0x10 key=keyblob_key)
 
 // Final keys:
 package1_key        /* slot11 */ = keyblob[0x80:0x90]
 master_key          /* slot12 */ = aes_unwrap(normalseed_retail, keyblob+0x20)
 new_master_key      /* slot14 */ = aes_unwrap(2dc1f48d.., keyblob+0x20)
 new_per_console_key /* slot13 */ = aes_unwrap(0c9109db.., old_keyblob_key)
 per_console_key     /* slot15 */ = aes_unwrap(4f025f0e.., old_keyblob_key)

.. and on 6.2.0, they moved key generation out of package1ldr, and into the Secure Monitor's boot section:

 clear_keyslots_other_than_12_13_and_14()
 
 old_keyblob_key /* slot15 */ = aes_unwrap(aes_unwrap(df206f59.., tsec_key /* slot12 */), sbk /* slot14 */)
 /* Previously, master_kek was stored at keyblob+0x20) */
 master_kek      /* slot13 */ = aes_unwrap(374b7729.. /* probably firmware specific */, tsec_root_key /* slot13 */) 
 
 clear_keyslot(12)
 
 // Final keys:
 new_master_key      /* slot12 */ = aes_unwrap(2dc1f48d.., master_kek)        
 master_key          /* slot13 */ = aes_unwrap(normalseed_retail, master_kek) 
 new_per_console_key /* slot14 */ = aes_unwrap(0c9109db.., old_keyblob_key)
 per_console_key     /* slot15 */ = aes_unwrap(4f025f0e.., old_keyblob_key)


SBK and SSK keyslots are cleared after keys have been generated.

See table above for which keys are console unique.

The key used to verify a keyblob's MAC is not the keyblob key but a key derived from it; this is likely part of an attempt to mitigate side-channel attacks as the MAC is an alterable part of the keyblob.

The bootloader only stores the hardcoded constants for the keyblob used in the current revision. Nintendo are withholding all the future hardcoded constants.

This means that if you have an attack on the bootloader, you need to re-preform it every time they move to a new keyblob.

Dumping the SBK and TSEC key of any single system should be enough to derive all key material on the system, prior to 6.2.0.

The key-derivation is described in more detail here.

Keyblob

There are 32 keyblobs written to NAND at factory, with each keyblob encrypted with a console-unique key derived from the console's SBK, the console's tsec key, and a constant specific to each keyblob.

Despite being encrypted with console unique keys, though, the decrypted keyblob contents are shared for all consoles.

Used keyblobs are as follows:

System version Used keyblob Used master static key encryption key in keyblob
1.0.0-2.3.0 1 1
3.0.0 2 1
3.0.1-3.0.2 3 1
4.0.0-4.1.0 4 1
5.0.0-5.1.0 5 1
6.0.0-6.1.0 6 1

Starting from 6.2.0, key generation no longer uses keyblobs.

Seeds

 normalseed_retail = d8a2410a...
 
 [1.0.0] wrapped_keyblob_key = df206f59...
 [1.0.0] simpleseed_dev0   = aff11423...
 [1.0.0] simpleseed_dev1   = 5e177ee1...
 [1.0.0] normalseed_dev    = 0542a0fd...
 
 [3.0.0] wrapped_keyblob_key = 0c25615d...  
 [3.0.0] simpleseed_dev0   = de00216a...
 [3.0.0] simpleseed_dev1   = 2db7c0a1...
 [3.0.0] normalseed_dev    = 678c5a03...
 
 [3.0.1] wrapped_keyblob_key = 337685ee...  
 [3.0.1] simpleseed_dev0   = e045f5ba...
 [3.0.1] simpleseed_dev1   = 84d92e0d...
 [3.0.1] normalseed_dev    = cd88155b...
 
 [4.0.0] wrapped_keyblob_key = 2d1f4880...

Versions

The key generation system has historically been revised several times. Each version is bound to a specific BCT public key and can be identified by its first byte as follows:

Version BCT public key's first byte Description
K1 0x11 Erista prototype development
K2 0xFB Erista prototype development
K3 0x4F Erista prototype development
K4 Erista prototype retail
K5 0x37 Erista development
K6 0xF7 Erista retail
M1 0xDD Mariko prototype development
M2 0xC3 Mariko development
M3 0x9B Mariko retail

Secure Monitor Init

On all versions, the key to decrypt Package2 is generated by decrypting a constant seed with the master key. The key is erased after use.

Additionally, starting from 4.0.0, the Secure Monitor init will decrypt another constant seed successively with a special per console key and a special static key passed by package1loader, to generate the firmware specific per-console key. The operation will erase these special keys passed by package1loader.

Secure Monitor

The secure monitor performs some runtime cryptographic operations. See SMC for what operations it provides.