mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
fusee_cpp: implement read/decryption of package2
This commit is contained in:
parent
dbad464323
commit
4355a2b036
3 changed files with 226 additions and 1 deletions
165
fusee_cpp/program/source/fusee_package2.cpp
Normal file
165
fusee_cpp/program/source/fusee_package2.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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 <exosphere.hpp>
|
||||
#include "fusee_package2.hpp"
|
||||
#include "fusee_key_derivation.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 Package2KeySource[se::AesBlockSize] = {
|
||||
0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7
|
||||
};
|
||||
|
||||
void PreparePackage2Key(int pkg2_slot, int key_generation) {
|
||||
/* Get keyslot for the desired master key. */
|
||||
const int master_slot = PrepareMasterKey(key_generation);
|
||||
|
||||
/* Load the package2 key into the desired keyslot. */
|
||||
se::SetEncryptedAesKey128(pkg2_slot, master_slot, Package2KeySource, sizeof(Package2KeySource));
|
||||
}
|
||||
|
||||
void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *iv, size_t iv_size, u8 key_generation) {
|
||||
/* Ensure that the SE sees consistent data. */
|
||||
hw::FlushDataCache(src, src_size);
|
||||
if (src != dst) {
|
||||
hw::FlushDataCache(dst, dst_size);
|
||||
}
|
||||
|
||||
/* Load the package2 key into the temporary keyslot. */
|
||||
PreparePackage2Key(pkg1::AesKeySlot_Temporary, key_generation);
|
||||
|
||||
/* Decrypt the data. */
|
||||
se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Temporary, src, src_size, iv, iv_size);
|
||||
|
||||
/* Clear the keyslot we just used. */
|
||||
se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary);
|
||||
|
||||
/* Ensure that the cpu sees consistent data. */
|
||||
hw::InvalidateDataCache(dst, dst_size);
|
||||
}
|
||||
|
||||
void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src) {
|
||||
constexpr int IvSize = 0x10;
|
||||
|
||||
/* Decrypt the header. */
|
||||
DecryptPackage2(dst, sizeof(*dst), std::addressof(src), sizeof(src), std::addressof(src), IvSize, src.GetKeyGeneration());
|
||||
|
||||
/* Copy back the iv, which encodes encrypted metadata. */
|
||||
std::memcpy(dst, std::addressof(src), IvSize);
|
||||
}
|
||||
|
||||
bool VerifyPackage2Meta(const pkg2::Package2Meta &meta) {
|
||||
/* Get the obfuscated metadata. */
|
||||
const size_t size = meta.GetSize();
|
||||
const u8 key_generation = meta.GetKeyGeneration();
|
||||
|
||||
/* Check that size is big enough for the header. */
|
||||
if (size <= sizeof(pkg2::Package2Header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the size isn't larger than what we allow. */
|
||||
if (size > pkg2::Package2SizeMax) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the key generation is one that we can use. */
|
||||
static_assert(pkg1::KeyGeneration_Count == 12);
|
||||
if (key_generation >= pkg1::KeyGeneration_Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check the magic number. */
|
||||
if (!crypto::IsSameBytes(meta.magic, pkg2::Package2Meta::Magic::String, sizeof(meta.magic))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check the payload alignments. */
|
||||
if ((meta.entrypoint % pkg2::PayloadAlignment) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pkg2::PayloadCount; ++i) {
|
||||
if ((meta.payload_sizes[i] % pkg2::PayloadAlignment) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that the sizes sum to the total. */
|
||||
if (size != sizeof(pkg2::Package2Header) + meta.payload_sizes[0] + meta.payload_sizes[1] + meta.payload_sizes[2]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the payloads do not overflow. */
|
||||
for (int i = 0; i < pkg2::PayloadCount; ++i) {
|
||||
if (meta.payload_offsets[i] > meta.payload_offsets[i] + meta.payload_sizes[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that no payloads overlap. */
|
||||
for (int i = 0; i < pkg2::PayloadCount - 1; ++i) {
|
||||
for (int j = i + 1; j < pkg2::PayloadCount; ++j) {
|
||||
if (util::HasOverlap(meta.payload_offsets[i], meta.payload_sizes[i], meta.payload_offsets[j], meta.payload_sizes[j])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether any payload contains the entrypoint. */
|
||||
for (int i = 0; i < pkg2::PayloadCount; ++i) {
|
||||
if (util::Contains(meta.payload_offsets[i], meta.payload_sizes[i], meta.entrypoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* No payload contains the entrypoint, so we're not valid. */
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DecryptPackage2(u8 *package2) {
|
||||
/* Decrypt package2 header. */
|
||||
pkg2::Package2Header *header = reinterpret_cast<pkg2::Package2Header *>(package2);
|
||||
{
|
||||
pkg2::Package2Header tmp = *header;
|
||||
DecryptPackage2Header(std::addressof(header->meta), tmp.meta);
|
||||
}
|
||||
|
||||
/* Check package2 magic. */
|
||||
if (!VerifyPackage2Meta(header->meta)) {
|
||||
ShowFatalError("Package2 meta is invalid!\n");
|
||||
}
|
||||
|
||||
/* Decrypt package2 payloads. */
|
||||
u8 *payload = package2 + sizeof(*header);
|
||||
const u8 key_generation = header->meta.GetKeyGeneration();
|
||||
for (int i = 0; i < pkg2::PayloadCount; ++i) {
|
||||
if (header->meta.payload_sizes[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DecryptPackage2(payload, header->meta.payload_sizes[i], payload, header->meta.payload_sizes[i], header->meta.payload_ivs[i], sizeof(header->meta.payload_ivs[i]), key_generation);
|
||||
|
||||
payload += header->meta.payload_sizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
23
fusee_cpp/program/source/fusee_package2.hpp
Normal file
23
fusee_cpp/program/source/fusee_package2.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 <vapours.hpp>
|
||||
#pragma once
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
void DecryptPackage2(u8 *package2);
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#include "fusee_emummc.hpp"
|
||||
#include "fusee_mmc.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
#include "fusee_package2.hpp"
|
||||
#include "fusee_malloc.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
|
||||
|
@ -333,6 +334,39 @@ namespace ams::nxboot {
|
|||
return target_firmware;
|
||||
}
|
||||
|
||||
u8 *LoadBootConfigAndPackage2() {
|
||||
Result result;
|
||||
|
||||
/* Load boot config. */
|
||||
if (R_FAILED((result = ReadPackage2(0, secmon::MemoryRegionPhysicalIramBootConfig.GetPointer<void>(), secmon::MemoryRegionPhysicalIramBootConfig.GetSize())))) {
|
||||
ShowFatalError("Failed to read boot config: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
/* Read package2 header. */
|
||||
u8 *package2;
|
||||
size_t package2_size;
|
||||
{
|
||||
constexpr s64 Package2Offset = __builtin_offsetof(pkg2::StorageLayout, package2_header);
|
||||
|
||||
pkg2::Package2Header header;
|
||||
if (R_FAILED((result = ReadPackage2(Package2Offset, std::addressof(header), sizeof(header))))) {
|
||||
ShowFatalError("Failed to read package2 header: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
package2_size = header.meta.GetSize();
|
||||
package2 = static_cast<u8 *>(AllocateAligned(util::AlignUp(package2_size, 0x4000), 0x4000));
|
||||
|
||||
if (R_FAILED((result = ReadPackage2(Package2Offset, package2, util::AlignUp(package2_size, 0x4000))))) {
|
||||
ShowFatalError("Failed to read package2: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrypt package2. */
|
||||
DecryptPackage2(package2);
|
||||
|
||||
return package2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetupAndStartHorizon() {
|
||||
|
@ -356,7 +390,9 @@ namespace ams::nxboot {
|
|||
const auto target_firmware = GetTargetFirmware(package1);
|
||||
AMS_UNUSED(target_firmware);
|
||||
|
||||
/* TODO: Read/decrypt package2. */
|
||||
/* Read/decrypt package2. */
|
||||
u8 * const package2 = LoadBootConfigAndPackage2();
|
||||
AMS_UNUSED(package2);
|
||||
|
||||
/* TODO: Setup warmboot firmware. */
|
||||
|
||||
|
@ -366,6 +402,7 @@ namespace ams::nxboot {
|
|||
/* NOTE: Security Engine unusable past this point. */
|
||||
|
||||
/* TODO: Build modified package2. */
|
||||
WaitForReboot();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue