Difference between revisions of "Cryptosystem"

From Nintendo Switch Brew
Jump to navigation Jump to search
m (oops, both keys are needed of course)
(→‎Versions: fix M1-M4)
 
(39 intermediate revisions by 10 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 0 ==
+
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.
  
 +
= Package1ldr =
 +
== Key table ==
 +
[1.0.0-3.0.2] During package1ldr:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 19: Line 61:
 
! Name
 
! Name
 
! Set by
 
! Set by
! Cleared by
 
 
! Per-console
 
! Per-console
 +
! Per-firmware
 
|-
 
|-
| 0xB
+
| 11
| Pk11DecryptionKey
+
| Package1Key
| [[Package1]]
+
| [[Package1#Package1ldr|Package1ldr]]
| [[Package1]]
 
 
| No
 
| No
 +
| Yes
 +
|-
 +
| 14
 +
| SecureBootKey
 +
| Bootrom
 +
| Yes
 +
| No
 +
|-
 +
| 15
 +
| SecureStorageKey
 +
| Bootrom
 +
| Yes
 +
| No
 +
|}
 +
 +
[1.0.0-3.0.2] After package1ldr:
 +
{| class="wikitable" border="1"
 
|-
 
|-
| 0xC
+
! Keyslot
 +
! Name
 +
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 
| MasterKey
 
| MasterKey
| [[Package1]]
+
| [[Package1#Package1ldr|Package1ldr]]
| Forever
+
| No
 +
| Yes, on security updates
 +
|-
 +
| 13
 +
| PerConsoleKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 +
|}
 +
 
 +
[4.0.0+] After package1ldr (Secure Monitor boot):
 +
{| class="wikitable" border="1"
 +
|-
 +
! Keyslot
 +
! Name
 +
! Set by
 +
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 +
| MasterKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes, on security updates
 +
|-
 +
| 13
 +
| PerConsoleKeyForFirmwareSpecificPerConsoleKeyGen
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 +
|-
 +
| 14
 +
| StaticKeyForFirmwareSpecificPerConsoleKeyGen
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| No
 +
| Yes, on security updates
 +
|-
 +
| 15
 +
| PerConsoleKey
 +
| [[Package1#Package1ldr|Package1ldr]]
 +
| Yes
 +
| No
 +
|}
 +
 
 +
[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
 
| No
 +
|}
 +
 +
[6.2.0+] After package1ldr/TSEC Payload (Secure Monitor boot):
 +
{| class="wikitable" border="1"
 
|-
 
|-
| 0xD
+
! Keyslot
| ConsoleKey
+
! Name
| [[Package1]]
+
! Set by
| Forever
+
! Per-console
 +
! Per-firmware
 +
|-
 +
| 12
 +
| TsecKey
 +
| [[TSEC#Payload|Package1ldr TSEC Firmware]]
 
| Yes
 
| Yes
 +
| No
 
|-
 
|-
| 0xE
+
| 13
 +
| TsecRootKey
 +
| [[TSEC#Payload|Package1ldr TSEC Firmware]]
 +
| No
 +
| Unknown
 +
|-
 +
| 14
 
| SecureBootKey
 
| SecureBootKey
 
| Bootrom
 
| Bootrom
| [[Package1]]
+
| Yes
 
| No
 
| No
 
|-
 
|-
| 0xF
+
| 15
 
| SecureStorageKey
 
| SecureStorageKey
 
| Bootrom
 
| Bootrom
| [[Package1]]
 
 
| Yes
 
| Yes
 +
| No
 
|}
 
|}
  
Bootloader stage 0 generates three keys. 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 generation ==
 +
Note: aes_unwrap(wrapped_key, wrap_key) is just another name for a single AES-128 block decryption.
  
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.
+
If bit0 of 0x7000FB94 is clear, it will initialize keys like this (probably used for internal development units only):
Both the master static key encryption key and the stage 2 key are stored in a keyblob. The keyblob format is described [[Flash_Filesystem#Keyblob|here]].
+
  // 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))
  
The 32 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).
+
[4.0.0+] Above method was removed.
  
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.
+
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 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.
  
Dumping the fuses and TSEC key of any single system would effectively bypass all of the above security mechanisms.
+
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 [[Package1#Key_generation|here]].
+
The key-derivation is described in more detail [[Package1#Key_generation|here]].
  
==== Table of used keyblobs ====
+
=== 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:
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
Line 86: 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 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:
 +
 
 +
{| 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
 +
| 0x19
 +
| Mariko prototype development
 +
|-
 +
| M2
 +
| 0xC3
 +
| Mariko development
 +
|-
 +
| M3
 +
| 0xDD
 +
| Mariko prototype retail (pre-6.0.0)
 +
|-
 +
| M4
 +
| 0x9B
 +
| Mariko retail
 +
|}
  
It is currently unknown what key generation the stage 2 bootloader does.
+
= 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. 
  
== Secure Monitor ==
+
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.
 
The secure monitor performs some runtime cryptographic operations. See [[SMC]] for what operations it provides.

Latest revision as of 22:29, 7 November 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 0x19 Mariko prototype development
M2 0xC3 Mariko development
M3 0xDD Mariko prototype retail (pre-6.0.0)
M4 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.