Atmosphere/fusee/fusee-secondary/src/package1.c
SciresM 17ca463c3f
ams: replace sept with tsec firmware (#1594)
* ams: replace sept with tsec firmware

This replaces sept with a custom tsec key derivation firmware.

NOTE: This does not use any TSEC exploits whatsoever; it is a well-signed
TSEC binary assembled with envyas and signed with the real cauth key.

For more details, contact SciresM#0524.

* fusee: only set SBK if it's readable
2021-08-20 13:13:29 -07:00

229 lines
7.9 KiB
C

/*
* Copyright (c) 2018-2020 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 <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "../../../fusee/common/log.h"
#include "package1.h"
#include "bct.h"
#include "se.h"
static const uint8_t custom_public_key[0x100] = {
0x59, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0xFF,
};
static bool is_custom_public_key_erista(const void *bct) {
return memcmp((const uint8_t *)bct + 0x210, custom_public_key, sizeof(custom_public_key)) == 0;
}
static bool is_custom_public_key_mariko(const void *bct) {
return memcmp((const uint8_t *)bct + 0x10, custom_public_key, sizeof(custom_public_key)) == 0;
}
bool package1_is_custom_public_key(const void *bct, bool mariko) {
if (mariko) {
return is_custom_public_key_mariko(bct);
} else {
return is_custom_public_key_erista(bct);
}
}
int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1loader_size, FILE *boot0) {
nvboot_config_table *bct; /* Normal firmware BCT, primary. TODO: check? */
nv_bootloader_info *pk1l_info; /* TODO: check? */
size_t fpos, pk1l_offset;
if (package1loader == NULL || package1loader_size == NULL || boot0 == NULL) {
errno = EINVAL;
return -1;
}
bct = malloc(sizeof(nvboot_config_table));
if (bct == NULL) {
errno = ENOMEM;
return -1;
}
pk1l_info = &bct->bootloader[0];
fpos = ftell(boot0);
/* Read the BCT. */
if (fread(bct, sizeof(nvboot_config_table), 1, boot0) == 0) {
free(bct);
return -1;
}
/* If custom public key, use bct-2 instead of bct 0. */
if (is_custom_public_key_erista(bct)) {
if (fseek(boot0, fpos + 0x4000 * 2, SEEK_SET) != 0) {
free(bct);
return -1;
}
if (fread(bct, sizeof(nvboot_config_table), 1, boot0) == 0) {
free(bct);
return -1;
}
}
if (bct->bootloader_used < 1 || pk1l_info->version < 1) {
free(bct);
errno = EILSEQ;
return -1;
}
*package1loader_size = pk1l_info->length;
pk1l_offset = 0x4000 * pk1l_info->start_blk + 0x200 * pk1l_info->start_page;
free(bct);
(*package1loader) = memalign(0x10000, *package1loader_size);
if (*package1loader == NULL) {
errno = ENOMEM;
return -1;
}
/* Read the pk1/pk1l, and skip the backup too. */
if (fseek(boot0, fpos + pk1l_offset, SEEK_SET) != 0) {
return -1;
}
if (fread(*package1loader, *package1loader_size, 1, boot0) == 0) {
return -1;
}
if (fseek(boot0, fpos + pk1l_offset + 2 * PACKAGE1LOADER_SIZE_MAX, SEEK_SET) != 0) {
return -1;
}
return 0;
}
int package1_read_and_parse_boot0_mariko(void **package1loader, size_t *package1loader_size, FILE *boot0) {
/* TODO: Actually parse BOOT0 to locate package1ldr. */
size_t fpos, pk1l_offset;
fpos = ftell(boot0);
pk1l_offset = 0x100000;
/* Read the OEM header. */
pk11_mariko_oem_header_t oem_header;
if (fseek(boot0, fpos + pk1l_offset, SEEK_SET) != 0) {
return -1;
}
if (fread(&oem_header, sizeof(oem_header), 1, boot0) == 0) {
return -1;
}
/* Sanity check the size. */
if (oem_header.bl_size < 2 * sizeof(package1loader_header_t)) {
return -1;
}
/* Set the size. */
*package1loader_size = oem_header.bl_size;
/* Allocate pk11. */
(*package1loader) = memalign(0x10000, *package1loader_size);
if (*package1loader == NULL) {
errno = ENOMEM;
return -1;
}
/* Read pk11. */
if (fread(*package1loader, *package1loader_size, 1, boot0) == 0) {
return -1;
}
/* Copy the plaintext header. */
package1loader_header_t dec_header;
memcpy(&dec_header, *package1loader, sizeof(dec_header));
/* Decrypt the binary. */
const uint8_t __attribute__((aligned(16))) iv[16] = {0};
se_aes_128_cbc_decrypt(KEYSLOT_SWITCH_BEK_MARIKO, *package1loader, *package1loader_size, *package1loader, *package1loader_size, iv);
/* Validate the decryption. */
if (memcmp(&dec_header, (const uint8_t *)*package1loader + sizeof(dec_header), sizeof(dec_header)) != 0) {
print(SCREEN_LOG_LEVEL_DEBUG, "Decrypted package1loader is invalid...\n");
return -1;
}
/* Copy out the first header. */
memcpy(*package1loader, &dec_header, sizeof(dec_header));
return 0;
}
bool package1_get_tsec_fw(void **tsec_fw, const void *package1loader, size_t package1loader_size) {
/* The TSEC firmware is always located at a 256-byte aligned address. */
/* We're looking for its 4 first bytes. */
const uint32_t *pos;
uintptr_t pk1l = (uintptr_t)package1loader;
for (pos = (const uint32_t *)pk1l; (uintptr_t)pos < pk1l + package1loader_size; pos += 0x40) {
if (*pos == 0xCF42004D) {
(*tsec_fw) = (void *)pos;
return true;
}
}
return false;
}
void *package1_get_warmboot_fw(const package1_header_t *package1) {
/*
The layout of pk1 changes between versions.
However, the secmon always starts by this erratum code:
https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/nvidia/tegra/common/aarch64/tegra_helpers.S#L312
and thus by 0xD5034FDF.
Nx-bootloader starts by 0xE328F0C0 (msr cpsr_f, 0xc0) before 6.2.0 and by 0xF0C0A7F0 afterwards.
*/
const uint32_t *data = (const uint32_t *)package1->data;
for (size_t i = 0; i < 3; i++) {
switch (*data) {
case 0xD5034FDFu:
data += package1->secmon_size / 4;
break;
case 0xE328F0C0:
case 0xF0C0A7F0:
data += package1->nx_bootloader_size / 4;
break;
default:
/* TODO: should we validate its signature? */
return (void *)data;
}
}
return NULL;
}