mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
fusee/exo: BYOK support for 6.2.0. Proper support TODO.
This commit is contained in:
parent
a71d98d78b
commit
767a4b3606
13 changed files with 343 additions and 63 deletions
|
@ -144,7 +144,7 @@ static void setup_se(void) {
|
||||||
decrypt_data_into_keyslot(KEYSLOT_SWITCH_6XTSECROOTKEY, KEYSLOT_SWITCH_6XTSECROOTKEY, new_master_kek_sources[0], 0x10);
|
decrypt_data_into_keyslot(KEYSLOT_SWITCH_6XTSECROOTKEY, KEYSLOT_SWITCH_6XTSECROOTKEY, new_master_kek_sources[0], 0x10);
|
||||||
decrypt_data_into_keyslot(KEYSLOT_SWITCH_MASTERKEY, KEYSLOT_SWITCH_6XTSECROOTKEY, masterkey_seed, 0x10);
|
decrypt_data_into_keyslot(KEYSLOT_SWITCH_MASTERKEY, KEYSLOT_SWITCH_6XTSECROOTKEY, masterkey_seed, 0x10);
|
||||||
decrypt_data_into_keyslot(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY, KEYSLOT_SWITCH_6XTSECROOTKEY, devicekek_4x_seed, 0x10);
|
decrypt_data_into_keyslot(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY, KEYSLOT_SWITCH_6XTSECROOTKEY, devicekek_4x_seed, 0x10);
|
||||||
clear_aes_keyslot(KEYSLOT_SWITCH_6XTSECROOTKEY);
|
clear_aes_keyslot(KEYSLOT_SWITCH_6XTSECROOTKEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect Master Key revision. */
|
/* Detect Master Key revision. */
|
||||||
|
@ -346,7 +346,7 @@ static bool validate_package2_metadata(package2_meta_t *metadata) {
|
||||||
|
|
||||||
/* Perform version checks. */
|
/* Perform version checks. */
|
||||||
/* We will be compatible with all package2s released before current, but not newer ones. */
|
/* We will be compatible with all package2s released before current, but not newer ones. */
|
||||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_600_CURRENT) {
|
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_620_CURRENT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +466,7 @@ static void copy_warmboot_bin_to_dram() {
|
||||||
warmboot_src = (uint8_t *)0x4003B000;
|
warmboot_src = (uint8_t *)0x4003B000;
|
||||||
break;
|
break;
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_600:
|
case EXOSPHERE_TARGET_FIRMWARE_600:
|
||||||
|
case EXOSPHERE_TARGET_FIRMWARE_620:
|
||||||
warmboot_src = (uint8_t *)0x4003D800;
|
warmboot_src = (uint8_t *)0x4003D800;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,8 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(void) {
|
||||||
#define PACKAGE2_MAXVER_302 0x5
|
#define PACKAGE2_MAXVER_302 0x5
|
||||||
#define PACKAGE2_MAXVER_400_410 0x6
|
#define PACKAGE2_MAXVER_400_410 0x6
|
||||||
#define PACKAGE2_MAXVER_500_510 0x7
|
#define PACKAGE2_MAXVER_500_510 0x7
|
||||||
#define PACKAGE2_MAXVER_600_CURRENT 0x8
|
#define PACKAGE2_MAXVER_600_610 0x8
|
||||||
|
#define PACKAGE2_MAXVER_620_CURRENT 0x9
|
||||||
|
|
||||||
#define PACKAGE2_MINVER_100 0x3
|
#define PACKAGE2_MINVER_100 0x3
|
||||||
#define PACKAGE2_MINVER_200 0x4
|
#define PACKAGE2_MINVER_200 0x4
|
||||||
|
@ -75,7 +76,8 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(void) {
|
||||||
#define PACKAGE2_MINVER_302 0x6
|
#define PACKAGE2_MINVER_302 0x6
|
||||||
#define PACKAGE2_MINVER_400_410 0x7
|
#define PACKAGE2_MINVER_400_410 0x7
|
||||||
#define PACKAGE2_MINVER_500_510 0x8
|
#define PACKAGE2_MINVER_500_510 0x8
|
||||||
#define PACKAGE2_MINVER_600_CURRENT 0x9
|
#define PACKAGE2_MINVER_600_610 0x9
|
||||||
|
#define PACKAGE2_MINVER_620_CURRENT 0xA
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
|
|
|
@ -159,6 +159,7 @@ void set_version_specific_smcs(void) {
|
||||||
break;
|
break;
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_500:
|
case EXOSPHERE_TARGET_FIRMWARE_500:
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_600:
|
case EXOSPHERE_TARGET_FIRMWARE_600:
|
||||||
|
case EXOSPHERE_TARGET_FIRMWARE_620:
|
||||||
/* No more LoadSecureExpModKey. */
|
/* No more LoadSecureExpModKey. */
|
||||||
g_smc_user_table[0xE].handler = NULL;
|
g_smc_user_table[0xE].handler = NULL;
|
||||||
g_smc_user_table[0xC].id = 0xC300D60C;
|
g_smc_user_table[0xC].id = 0xC300D60C;
|
||||||
|
|
|
@ -49,6 +49,7 @@ static bool is_user_keyslot_valid(unsigned int keyslot) {
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_500:
|
case EXOSPHERE_TARGET_FIRMWARE_500:
|
||||||
return keyslot <= 3;
|
return keyslot <= 3;
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_600:
|
case EXOSPHERE_TARGET_FIRMWARE_600:
|
||||||
|
case EXOSPHERE_TARGET_FIRMWARE_620:
|
||||||
default:
|
default:
|
||||||
return keyslot <= 5;
|
return keyslot <= 5;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +167,7 @@ uint32_t user_generate_aes_kek(smc_args_t *args) {
|
||||||
/* 5.0.0+ Bounds checking. */
|
/* 5.0.0+ Bounds checking. */
|
||||||
if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) {
|
if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) {
|
||||||
if (is_personalized) {
|
if (is_personalized) {
|
||||||
if (master_key_rev > MASTERKEY_REVISION_MAX || (MASTERKEY_REVISION_300 <= master_key_rev + 1 && master_key_rev + 1 < MASTERKEY_REVISION_400_410)) {
|
if (master_key_rev >= MASTERKEY_REVISION_MAX || (MASTERKEY_REVISION_300 <= master_key_rev && master_key_rev < MASTERKEY_REVISION_400_410)) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (mask_id > 3 || usecase >= CRYPTOUSECASE_MAX_5X) {
|
if (mask_id > 3 || usecase >= CRYPTOUSECASE_MAX_5X) {
|
||||||
|
|
216
fusee/fusee-secondary/src/extkeys.c
Normal file
216
fusee/fusee-secondary/src/extkeys.c
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "extkeys.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a line from file f and parses out the key and value from it.
|
||||||
|
* The format of a line must match /^ *[A-Za-z0-9_] *[,=] *.+$/.
|
||||||
|
* If a line ends in \r, the final \r is stripped.
|
||||||
|
* The input file is assumed to have been opened with the 'b' flag.
|
||||||
|
* The input file is assumed to contain only ASCII.
|
||||||
|
*
|
||||||
|
* A line cannot exceed 512 bytes in length.
|
||||||
|
* Lines that are excessively long will be silently truncated.
|
||||||
|
*
|
||||||
|
* On success, *key and *value will be set to point to the key and value in
|
||||||
|
* the input line, respectively.
|
||||||
|
* *key and *value may also be NULL in case of empty lines.
|
||||||
|
* On failure, *key and *value will be set to NULL.
|
||||||
|
* End of file is considered failure.
|
||||||
|
*
|
||||||
|
* Because *key and *value will point to a static buffer, their contents must be
|
||||||
|
* copied before calling this function again.
|
||||||
|
* For the same reason, this function is not thread-safe.
|
||||||
|
*
|
||||||
|
* The key will be converted to lowercase.
|
||||||
|
* An empty key is considered a parse error, but an empty value is returned as
|
||||||
|
* success.
|
||||||
|
*
|
||||||
|
* This function assumes that the file can be trusted not to contain any NUL in
|
||||||
|
* the contents.
|
||||||
|
*
|
||||||
|
* Whitespace (' ', ASCII 0x20, as well as '\t', ASCII 0x09) at the beginning of
|
||||||
|
* the line, at the end of the line as well as around = (or ,) will be ignored.
|
||||||
|
*
|
||||||
|
* @param f the file to read
|
||||||
|
* @param key pointer to change to point to the key
|
||||||
|
* @param value pointer to change to point to the value
|
||||||
|
* @return 0 on success,
|
||||||
|
* 1 on end of file,
|
||||||
|
* -1 on parse error (line too long, line malformed)
|
||||||
|
* -2 on I/O error
|
||||||
|
*/
|
||||||
|
static int get_kv(FILE *f, char **key, char **value) {
|
||||||
|
#define SKIP_SPACE(p) do {\
|
||||||
|
for (; *p == ' ' || *p == '\t'; ++p)\
|
||||||
|
;\
|
||||||
|
} while(0);
|
||||||
|
static char line[1024];
|
||||||
|
char *k, *v, *p, *end;
|
||||||
|
|
||||||
|
*key = *value = NULL;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (fgets(line, (int)sizeof(line), f) == NULL) {
|
||||||
|
if (feof(f))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (errno != 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
if (*line == '\n' || *line == '\r' || *line == '\0')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Not finding \r or \n is not a problem.
|
||||||
|
* The line might just be exactly 512 characters long, we have no way to
|
||||||
|
* tell.
|
||||||
|
* Additionally, it's possible that the last line of a file is not actually
|
||||||
|
* a line (i.e., does not end in '\n'); we do want to handle those.
|
||||||
|
*/
|
||||||
|
if ((p = strchr(line, '\r')) != NULL || (p = strchr(line, '\n')) != NULL) {
|
||||||
|
end = p;
|
||||||
|
*p = '\0';
|
||||||
|
} else {
|
||||||
|
end = line + strlen(line) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = line;
|
||||||
|
SKIP_SPACE(p);
|
||||||
|
k = p;
|
||||||
|
|
||||||
|
/* Validate key and convert to lower case. */
|
||||||
|
for (; *p != ' ' && *p != ',' && *p != '\t' && *p != '='; ++p) {
|
||||||
|
if (*p == '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (*p >= 'A' && *p <= 'Z') {
|
||||||
|
*p = 'a' + (*p - 'A');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p != '_' &&
|
||||||
|
(*p < '0' || *p > '9') &&
|
||||||
|
(*p < 'a' || *p > 'z')) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bail if the final ++p put us at the end of string */
|
||||||
|
if (*p == '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* We should be at the end of key now and either whitespace or [,=]
|
||||||
|
* follows.
|
||||||
|
*/
|
||||||
|
if (*p == '=' || *p == ',') {
|
||||||
|
*p++ = '\0';
|
||||||
|
} else {
|
||||||
|
*p++ = '\0';
|
||||||
|
SKIP_SPACE(p);
|
||||||
|
if (*p != '=' && *p != ',')
|
||||||
|
return -1;
|
||||||
|
*p++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty key is an error. */
|
||||||
|
if (*k == '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SKIP_SPACE(p);
|
||||||
|
v = p;
|
||||||
|
|
||||||
|
/* Skip trailing whitespace */
|
||||||
|
for (p = end - 1; *p == '\t' || *p == ' '; --p)
|
||||||
|
;
|
||||||
|
|
||||||
|
*(p + 1) = '\0';
|
||||||
|
|
||||||
|
*key = k;
|
||||||
|
*value = v;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#undef SKIP_SPACE
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ishex(char c) {
|
||||||
|
if ('a' <= c && c <= 'f') return 1;
|
||||||
|
if ('A' <= c && c <= 'F') return 1;
|
||||||
|
if ('0' <= c && c <= '9') return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char hextoi(char c) {
|
||||||
|
if ('a' <= c && c <= 'f') return c - 'a' + 0xA;
|
||||||
|
if ('A' <= c && c <= 'F') return c - 'A' + 0xA;
|
||||||
|
if ('0' <= c && c <= '9') return c - '0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_hex_key(unsigned char *key, const char *hex, unsigned int len) {
|
||||||
|
if (strlen(hex) != 2 * len) {
|
||||||
|
fatal_error("Key (%s) must be %x hex digits!\n", hex, 2 * len);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < 2 * len; i++) {
|
||||||
|
if (!ishex(hex[i])) {
|
||||||
|
fatal_error("Key (%s) must be %x hex digits!\n", hex, 2 * len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(key, 0, len);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < 2 * len; i++) {
|
||||||
|
char val = hextoi(hex[i]);
|
||||||
|
if ((i & 1) == 0) {
|
||||||
|
val <<= 4;
|
||||||
|
}
|
||||||
|
key[i >> 1] |= val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void extkeys_initialize_keyset(fusee_extkeys_t *keyset, FILE *f) {
|
||||||
|
char *key, *value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) {
|
||||||
|
if (ret == 0) {
|
||||||
|
if (key == NULL || value == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int matched_key = 0;
|
||||||
|
if (strcmp(key, "tsec_root_key") == 0 || strcmp(key, "tsec_root_key_00") == 0) {
|
||||||
|
parse_hex_key(keyset->tsec_root_key, value, sizeof(keyset->tsec_root_key));
|
||||||
|
matched_key = 1;
|
||||||
|
} else {
|
||||||
|
char test_name[0x100] = {0};
|
||||||
|
for (unsigned int i = 0; i < 0x20 && !matched_key; i++) {
|
||||||
|
snprintf(test_name, sizeof(test_name), "master_kek_%02x", i);
|
||||||
|
if (strcmp(key, test_name) == 0) {
|
||||||
|
parse_hex_key(keyset->master_keks[i], value, sizeof(keyset->master_keks[i]));
|
||||||
|
matched_key = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
fusee/fusee-secondary/src/extkeys.h
Normal file
32
fusee/fusee-secondary/src/extkeys.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSEE_EXTKEYS_H
|
||||||
|
#define FUSEE_EXTKEYS_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "utils.h"
|
||||||
|
#include "masterkey.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char tsec_root_key[0x10];
|
||||||
|
unsigned char master_keks[0x20][0x10];
|
||||||
|
} fusee_extkeys_t;
|
||||||
|
|
||||||
|
void parse_hex_key(unsigned char *key, const char *hex, unsigned int len);
|
||||||
|
void extkeys_initialize_keyset(fusee_extkeys_t *keyset, FILE *f);
|
||||||
|
|
||||||
|
#endif
|
|
@ -13,13 +13,15 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include "key_derivation.h"
|
#include "key_derivation.h"
|
||||||
#include "masterkey.h"
|
#include "masterkey.h"
|
||||||
#include "se.h"
|
#include "se.h"
|
||||||
#include "exocfg.h"
|
#include "exocfg.h"
|
||||||
#include "fuse.h"
|
#include "fuse.h"
|
||||||
#include "tsec.h"
|
#include "tsec.h"
|
||||||
|
#include "extkeys.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#define AL16 ALIGN(16)
|
#define AL16 ALIGN(16)
|
||||||
|
@ -53,6 +55,10 @@ static const uint8_t AL16 masterkey_4x_seed[0x10] = {
|
||||||
0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66
|
0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint8_t AL16 new_master_kek_seeds[1][0x10] = {
|
||||||
|
{0x37, 0x4B, 0x77, 0x29, 0x59, 0xB4, 0x04, 0x30, 0x81, 0xF6, 0xE5, 0x8C, 0x6D, 0x36, 0x17, 0x9A}, /* MasterKek seed 06. */
|
||||||
|
};
|
||||||
|
|
||||||
static nx_dec_keyblob_t AL16 g_dec_keyblobs[32];
|
static nx_dec_keyblob_t AL16 g_dec_keyblobs[32];
|
||||||
|
|
||||||
static int get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size, uint32_t tsec_key_id) {
|
static int get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size, uint32_t tsec_key_id) {
|
||||||
|
@ -108,45 +114,79 @@ static int decrypt_keyblob(const nx_keyblob_t *keyblobs, uint32_t revision, uint
|
||||||
}
|
}
|
||||||
|
|
||||||
int load_package1_key(uint32_t revision) {
|
int load_package1_key(uint32_t revision) {
|
||||||
if (revision > MASTERKEY_REVISION_600_CURRENT) {
|
if (revision > MASTERKEY_REVISION_600_610) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_aes_keyslot(0xB, g_dec_keyblobs[revision].keys[8], 0x10);
|
set_aes_keyslot(0xB, g_dec_keyblobs[revision].package1_key, 0x10);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derive all Switch keys. */
|
/* Derive all Switch keys. */
|
||||||
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size) {
|
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size, unsigned int *out_keygen_type) {
|
||||||
|
uint8_t AL16 tsec_key[0x10];
|
||||||
uint8_t AL16 work_buffer[0x10];
|
uint8_t AL16 work_buffer[0x10];
|
||||||
|
uint8_t AL16 zeroes[0x10] = {0};
|
||||||
|
|
||||||
/* TODO: Set keyslot flags properly in preparation of derivation. */
|
/* TODO: Set keyslot flags properly in preparation of derivation. */
|
||||||
set_aes_keyslot_flags(0xE, 0x15);
|
set_aes_keyslot_flags(0xE, 0x15);
|
||||||
set_aes_keyslot_flags(0xD, 0x15);
|
set_aes_keyslot_flags(0xD, 0x15);
|
||||||
|
|
||||||
/* Set TSEC key. */
|
/* Set TSEC key. */
|
||||||
if (get_tsec_key(work_buffer, tsec_fw, tsec_fw_size, 1) != 0) {
|
if (get_tsec_key(tsec_key, tsec_fw, tsec_fw_size, 1) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_aes_keyslot(0xD, work_buffer, 0x10);
|
set_aes_keyslot(0xD, tsec_key, 0x10);
|
||||||
|
|
||||||
/* Decrypt all keyblobs, setting keyslot 0xF correctly. */
|
/* Decrypt all keyblobs, setting keyslot 0xF correctly. */
|
||||||
for (unsigned int rev = 0; rev < MASTERKEY_REVISION_MAX; rev++) {
|
for (unsigned int rev = 0; rev <= MASTERKEY_REVISION_600_610; rev++) {
|
||||||
int ret = decrypt_keyblob(keyblobs, rev, available_revision);
|
int ret = decrypt_keyblob(keyblobs, rev, available_revision);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: Eventually do 6.2.0+ keygen properly? */
|
||||||
|
*out_keygen_type = 0;
|
||||||
|
if (target_firmware >= EXOSPHERE_TARGET_FIRMWARE_620) {
|
||||||
|
const char *keyfile = fuse_get_retail_type() != 0 ? "atmosphere/prod.keys" : "atmosphere/dev.keys";
|
||||||
|
FILE *extkey_file = fopen(keyfile, "r");
|
||||||
|
AL16 fusee_extkeys_t extkeys = {0};
|
||||||
|
if (extkey_file == NULL) {
|
||||||
|
fatal_error("Error: failed to read %s, needed for 6.2.0+ key derivation!", keyfile);
|
||||||
|
}
|
||||||
|
extkeys_initialize_keyset(&extkeys, extkey_file);
|
||||||
|
fclose(extkey_file);
|
||||||
|
|
||||||
|
if (memcmp(extkeys.tsec_root_key, zeroes, 0x10) != 0) {
|
||||||
|
set_aes_keyslot(0xC, extkeys.tsec_root_key, 0x10);
|
||||||
|
for (unsigned int rev = MASTERKEY_REVISION_620_CURRENT; rev < MASTERKEY_REVISION_MAX; rev++) {
|
||||||
|
se_aes_ecb_decrypt_block(0xC, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620_CURRENT], 0x10);
|
||||||
|
memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int rev = MASTERKEY_REVISION_620_CURRENT; rev < MASTERKEY_REVISION_MAX; rev++) {
|
||||||
|
memcpy(g_dec_keyblobs[rev].master_kek, extkeys.master_keks[rev], 0x10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(g_dec_keyblobs[available_revision].master_kek, zeroes, 0x10) == 0) {
|
||||||
|
fatal_error("Error: failed to derive master_kek_%02x!", available_revision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear the SBK. */
|
/* Clear the SBK. */
|
||||||
clear_aes_keyslot(0xE);
|
clear_aes_keyslot(0xE);
|
||||||
|
|
||||||
/* Get needed data. */
|
/* Get needed data. */
|
||||||
set_aes_keyslot(0xC, g_dec_keyblobs[MASTERKEY_REVISION_600_CURRENT].keys[0], 0x10);
|
set_aes_keyslot(0xC, g_dec_keyblobs[available_revision].master_kek, 0x10);
|
||||||
|
|
||||||
/* Also set the Package1 key for the revision that is stored on the eMMC boot0 partition. */
|
/* Also set the Package1 key for the revision that is stored on the eMMC boot0 partition. */
|
||||||
load_package1_key(available_revision);
|
if (target_firmware < EXOSPHERE_TARGET_FIRMWARE_620) {
|
||||||
|
load_package1_key(available_revision);
|
||||||
|
}
|
||||||
|
|
||||||
/* Derive keys for Exosphere, lock critical keyslots. */
|
/* Derive keys for Exosphere, lock critical keyslots. */
|
||||||
switch (target_firmware) {
|
switch (target_firmware) {
|
||||||
|
@ -164,6 +204,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
|
||||||
break;
|
break;
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_500:
|
case EXOSPHERE_TARGET_FIRMWARE_500:
|
||||||
case EXOSPHERE_TARGET_FIRMWARE_600:
|
case EXOSPHERE_TARGET_FIRMWARE_600:
|
||||||
|
case EXOSPHERE_TARGET_FIRMWARE_620:
|
||||||
decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10);
|
decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10);
|
||||||
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
|
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
|
||||||
decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10);
|
decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10);
|
||||||
|
|
|
@ -28,7 +28,14 @@ typedef enum BisPartition {
|
||||||
} BisPartition;
|
} BisPartition;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t keys[9][0x10];
|
union {
|
||||||
|
uint8_t keys[9][0x10];
|
||||||
|
struct {
|
||||||
|
uint8_t master_kek[0x10];
|
||||||
|
uint8_t _keys[7][0x10];
|
||||||
|
uint8_t package1_key[0x10];
|
||||||
|
};
|
||||||
|
};
|
||||||
} nx_dec_keyblob_t;
|
} nx_dec_keyblob_t;
|
||||||
|
|
||||||
typedef struct nx_keyblob_t {
|
typedef struct nx_keyblob_t {
|
||||||
|
@ -40,7 +47,7 @@ typedef struct nx_keyblob_t {
|
||||||
};
|
};
|
||||||
} nx_keyblob_t;
|
} nx_keyblob_t;
|
||||||
|
|
||||||
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size);
|
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size, unsigned int *out_keygen_type);
|
||||||
int load_package1_key(uint32_t revision);
|
int load_package1_key(uint32_t revision);
|
||||||
void finalize_nx_keydata(uint32_t target_firmware);
|
void finalize_nx_keydata(uint32_t target_firmware);
|
||||||
void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware);
|
void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware);
|
||||||
|
|
|
@ -26,7 +26,6 @@ static unsigned int g_mkey_revision = 0;
|
||||||
static bool g_determined_mkey_revision = false;
|
static bool g_determined_mkey_revision = false;
|
||||||
|
|
||||||
static uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10];
|
static uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10];
|
||||||
static uint8_t g_old_devicekeys[MASTERKEY_NUM_NEW_DEVICE_KEYS - 1][0x10];
|
|
||||||
|
|
||||||
/* TODO: Extend with new vectors, as needed. */
|
/* TODO: Extend with new vectors, as needed. */
|
||||||
/* Dev unit keys. */
|
/* Dev unit keys. */
|
||||||
|
@ -38,6 +37,7 @@ static const uint8_t mkey_vectors_dev[MASTERKEY_REVISION_MAX][0x10] =
|
||||||
{0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */
|
{0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */
|
||||||
{0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */
|
{0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */
|
||||||
{0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */
|
{0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */
|
||||||
|
{0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Retail unit keys. */
|
/* Retail unit keys. */
|
||||||
|
@ -49,6 +49,7 @@ static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] =
|
||||||
{0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */
|
{0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */
|
||||||
{0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */
|
{0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */
|
||||||
{0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */
|
{0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */
|
||||||
|
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool check_mkey_revision(unsigned int revision, bool is_retail) {
|
static bool check_mkey_revision(unsigned int revision, bool is_retail) {
|
||||||
|
@ -119,33 +120,3 @@ unsigned int mkey_get_keyslot(unsigned int revision) {
|
||||||
return KEYSLOT_SWITCH_TEMPKEY;
|
return KEYSLOT_SWITCH_TEMPKEY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_old_devkey(unsigned int revision, const uint8_t *key) {
|
|
||||||
if (revision < MASTERKEY_REVISION_400_410 || MASTERKEY_REVISION_600_CURRENT <= revision) {
|
|
||||||
generic_panic();
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], key, 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int devkey_get_keyslot(unsigned int revision) {
|
|
||||||
if (!g_determined_mkey_revision || revision >= MASTERKEY_REVISION_MAX) {
|
|
||||||
generic_panic();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revision > g_mkey_revision) {
|
|
||||||
generic_panic();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revision >= 1) {
|
|
||||||
if (revision == MASTERKEY_REVISION_600_CURRENT) {
|
|
||||||
return KEYSLOT_SWITCH_DEVICEKEY;
|
|
||||||
} else {
|
|
||||||
/* Load into a temp keyslot. */
|
|
||||||
set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], 0x10);
|
|
||||||
return KEYSLOT_SWITCH_TEMPKEY;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return KEYSLOT_SWITCH_4XOLDDEVICEKEY;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,15 +19,16 @@
|
||||||
|
|
||||||
/* This is glue code to enable master key support across versions. */
|
/* This is glue code to enable master key support across versions. */
|
||||||
|
|
||||||
/* TODO: Update to 0x7 on release of new master key. */
|
/* TODO: Update to 0x8 on release of new master key. */
|
||||||
#define MASTERKEY_REVISION_MAX 0x6
|
#define MASTERKEY_REVISION_MAX 0x7
|
||||||
|
|
||||||
#define MASTERKEY_REVISION_100_230 0x00
|
#define MASTERKEY_REVISION_100_230 0x00
|
||||||
#define MASTERKEY_REVISION_300 0x01
|
#define MASTERKEY_REVISION_300 0x01
|
||||||
#define MASTERKEY_REVISION_301_302 0x02
|
#define MASTERKEY_REVISION_301_302 0x02
|
||||||
#define MASTERKEY_REVISION_400_410 0x03
|
#define MASTERKEY_REVISION_400_410 0x03
|
||||||
#define MASTERKEY_REVISION_500_510 0x04
|
#define MASTERKEY_REVISION_500_510 0x04
|
||||||
#define MASTERKEY_REVISION_600_CURRENT 0x05
|
#define MASTERKEY_REVISION_600_610 0x05
|
||||||
|
#define MASTERKEY_REVISION_620_CURRENT 0x06
|
||||||
|
|
||||||
#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410)
|
#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410)
|
||||||
|
|
||||||
|
|
|
@ -90,12 +90,16 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nxboot_configure_exosphere(uint32_t target_firmware) {
|
static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int keygen_type) {
|
||||||
exosphere_config_t exo_cfg = {0};
|
exosphere_config_t exo_cfg = {0};
|
||||||
|
|
||||||
exo_cfg.magic = MAGIC_EXOSPHERE_BOOTCONFIG;
|
exo_cfg.magic = MAGIC_EXOSPHERE_BOOTCONFIG;
|
||||||
exo_cfg.target_firmware = target_firmware;
|
exo_cfg.target_firmware = target_firmware;
|
||||||
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT;
|
if (keygen_type) {
|
||||||
|
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT | EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
|
||||||
|
} else {
|
||||||
|
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
if (ini_parse_string(get_loader_ctx()->bct0, exosphere_ini_handler, &exo_cfg) < 0) {
|
if (ini_parse_string(get_loader_ctx()->bct0, exosphere_ini_handler, &exo_cfg) < 0) {
|
||||||
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
|
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
|
||||||
|
@ -307,8 +311,14 @@ uint32_t nxboot_main(void) {
|
||||||
|
|
||||||
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loaded firmware from eMMC...\n");
|
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loaded firmware from eMMC...\n");
|
||||||
|
|
||||||
|
/* Derive keydata. */
|
||||||
|
unsigned int keygen_type = 0;
|
||||||
|
if (derive_nx_keydata(target_firmware, g_keyblobs, available_revision, tsec_fw, tsec_fw_size, &keygen_type) != 0) {
|
||||||
|
fatal_error("[NXBOOT]: Key derivation failed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* Setup boot configuration for Exosphère. */
|
/* Setup boot configuration for Exosphère. */
|
||||||
nxboot_configure_exosphere(target_firmware);
|
nxboot_configure_exosphere(target_firmware, keygen_type);
|
||||||
|
|
||||||
/* Initialize Boot Reason on older firmware versions. */
|
/* Initialize Boot Reason on older firmware versions. */
|
||||||
if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) {
|
if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) {
|
||||||
|
@ -316,11 +326,6 @@ uint32_t nxboot_main(void) {
|
||||||
nxboot_set_bootreason();
|
nxboot_set_bootreason();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derive keydata. */
|
|
||||||
if (derive_nx_keydata(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware, g_keyblobs, available_revision, tsec_fw, tsec_fw_size) != 0) {
|
|
||||||
fatal_error("[NXBOOT]: Key derivation failed!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read the warmboot firmware from a file, otherwise from PK1. */
|
/* Read the warmboot firmware from a file, otherwise from PK1. */
|
||||||
if (loader_ctx->warmboot_path[0] != '\0') {
|
if (loader_ctx->warmboot_path[0] != '\0') {
|
||||||
warmboot_fw_size = get_file_size(loader_ctx->warmboot_path);
|
warmboot_fw_size = get_file_size(loader_ctx->warmboot_path);
|
||||||
|
|
|
@ -214,7 +214,7 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[]
|
||||||
|
|
||||||
/* Perform version checks. */
|
/* Perform version checks. */
|
||||||
/* We will be compatible with all package2s released before current, but not newer ones. */
|
/* We will be compatible with all package2s released before current, but not newer ones. */
|
||||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_600_CURRENT) {
|
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_620_CURRENT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
#define PACKAGE2_MAXVER_302 0x5
|
#define PACKAGE2_MAXVER_302 0x5
|
||||||
#define PACKAGE2_MAXVER_400_410 0x6
|
#define PACKAGE2_MAXVER_400_410 0x6
|
||||||
#define PACKAGE2_MAXVER_500_510 0x7
|
#define PACKAGE2_MAXVER_500_510 0x7
|
||||||
#define PACKAGE2_MAXVER_600_CURRENT 0x8
|
#define PACKAGE2_MAXVER_600_610 0x8
|
||||||
|
#define PACKAGE2_MAXVER_620_CURRENT 0x9
|
||||||
|
|
||||||
#define PACKAGE2_MINVER_100 0x3
|
#define PACKAGE2_MINVER_100 0x3
|
||||||
#define PACKAGE2_MINVER_200 0x4
|
#define PACKAGE2_MINVER_200 0x4
|
||||||
|
@ -42,7 +43,8 @@
|
||||||
#define PACKAGE2_MINVER_302 0x6
|
#define PACKAGE2_MINVER_302 0x6
|
||||||
#define PACKAGE2_MINVER_400_410 0x7
|
#define PACKAGE2_MINVER_400_410 0x7
|
||||||
#define PACKAGE2_MINVER_500_510 0x8
|
#define PACKAGE2_MINVER_500_510 0x8
|
||||||
#define PACKAGE2_MINVER_600_CURRENT 0x9
|
#define PACKAGE2_MINVER_600_610 0x9
|
||||||
|
#define PACKAGE2_MINVER_620_CURRENT 0xA
|
||||||
|
|
||||||
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue