diff --git a/libraries/libstratosphere/source/updater/updater_api.cpp b/libraries/libstratosphere/source/updater/updater_api.cpp index 014ea117e..879a0eeec 100644 --- a/libraries/libstratosphere/source/updater/updater_api.cpp +++ b/libraries/libstratosphere/source/updater/updater_api.cpp @@ -158,9 +158,16 @@ namespace ams::updater { R_TRY(boot0_accessor.Initialize()); ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to validate BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); + /* Compare BCT hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + if (!custom_public_key) { + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + } /* Compare BCT Sub hashes. */ R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalSub)); @@ -213,10 +220,16 @@ namespace ams::updater { R_TRY(boot1_accessor.Initialize()); ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to validate BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); /* Compare BCT hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + if (!custom_public_key) { + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + } /* Compare BCT Sub hashes. */ R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeSub)); @@ -260,6 +273,11 @@ namespace ams::updater { R_TRY(boot0_accessor.Initialize()); ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to update BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); + /* Write Package1 sub. */ R_TRY(boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); R_TRY(boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); @@ -282,11 +300,15 @@ namespace ams::updater { if (HasAutoRcmPreserve(boot_image_update_type) && !exosphere::IsRcmBugPatched()) { R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalSub)); R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + } } else { R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + } } } @@ -321,6 +343,10 @@ namespace ams::updater { R_TRY(boot1_accessor.Initialize()); ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to update BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); /* Write Package1 sub. */ R_TRY(boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); @@ -343,11 +369,15 @@ namespace ams::updater { if (HasAutoRcmPreserve(boot_image_update_type) && !exosphere::IsRcmBugPatched()) { R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeSub)); R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + } } else { R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + } } } diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.cpp b/libraries/libstratosphere/source/updater/updater_bis_management.cpp index b643634bf..0822f8ec7 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.cpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.cpp @@ -18,6 +18,36 @@ namespace ams::updater { + namespace { + + /* Recognize special public key (https://gist.github.com/SciresM/16b63ac1d80494522bdba2c57995257c). */ + /* P = 19 */ + /* Q = 1696986749729493925354392349339746171297507422986462747526968361144447230710192316397327889522451749459854070558277878297255552508603806832852079596337539247651161831569525505882103311631577368514276343192042634740927726070847704397913856975832811679847928433261678072951551065705680482548543833651752439700272736498378724153330763357721354498194000536297732323628263256733931353143625854828275237159155585342783077681713929284136658773985266864804093157854331138230313706015557050002740810464618031715670281442110238274404626065924786185264268216336867948322976979393032640085259926883014490947373494538254895109731 */ + /* N = 0xFF696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696959 */ + /* E = 0x10001 */ + /* D = 6512128715229088976470211610075969347035078304643231077895577077900787352712063823560162578441773733649014439616165727455431015055675770987914713980812453585413988983206576233689754710500864883529402371292948326392791238474661859182717295176679567362482790015587820446999760239570255254879359445627372805817473978644067558931078225451477635089763009580492462097185005355990612929951162366081631888011031830742459571000203341001926135389508196521518687349554188686396554248868403128728646457247197407637887195043486221295751496667162366700477934591694110831002874992896076061627516220934290742867397720103040314639313 */ + + constexpr const u8 CustomPublicKey[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, + }; + + } + Result BisAccessor::Initialize() { return fs::OpenBisPartition(std::addressof(this->storage), this->partition_id); } @@ -133,11 +163,36 @@ namespace ams::updater { size_t read_size; R_TRY(this->Read(&read_size, work_buffer, BctSize, which)); - void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffset); - void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffset); + /* NOTE: AutoRcm is only viable on Erista, so hardcode erista offsets. */ + void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffsetErista); + void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffsetErista); std::memcpy(dst_pubk, src_pubk, BctPubkSize); return ResultSuccess(); } + Result Boot0Accessor::DetectCustomPublicKey(bool *out, void *work_buffer, BootImageUpdateType boot_image_update_type) { + std::memset(work_buffer, 0, BctSize); + + const size_t pubk_offset = GetBctPubkOffset(boot_image_update_type); + + size_t read_size; + R_TRY(this->Read(&read_size, work_buffer, BctSize, Boot0Partition::BctNormalMain)); + + if (std::memcmp(reinterpret_cast(reinterpret_cast(work_buffer) + pubk_offset), CustomPublicKey, sizeof(CustomPublicKey)) != 0) { + *out = false; + return ResultSuccess(); + } + + R_TRY(this->Read(&read_size, work_buffer, BctSize, Boot0Partition::BctSafeMain)); + + if (std::memcmp(reinterpret_cast(reinterpret_cast(work_buffer) + pubk_offset), CustomPublicKey, sizeof(CustomPublicKey)) != 0) { + *out = false; + return ResultSuccess(); + } + + *out = true; + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.hpp b/libraries/libstratosphere/source/updater/updater_bis_management.hpp index c98333f6e..868285f00 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.hpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.hpp @@ -199,7 +199,8 @@ namespace ams::updater { class Boot0Accessor : public PartitionAccessor { public: static constexpr fs::BisPartitionId PartitionId = fs::BisPartitionId::BootPartition1Root; - static constexpr size_t BctPubkOffset = 0x210; + static constexpr size_t BctPubkOffsetErista = 0x210; + static constexpr size_t BctPubkOffsetMariko = 0x10; static constexpr size_t BctPubkSize = 0x100; static constexpr size_t BctEksOffset = 0x450; static constexpr size_t BctVersionOffset = 0x2330; @@ -210,10 +211,22 @@ namespace ams::updater { static size_t GetBootloaderVersion(void *bct); static size_t GetEksIndex(size_t bootloader_version); static void CopyEks(void *dst_bct, const void *src_eks, size_t eks_index); + + static size_t GetBctPubkOffset(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + return BctPubkOffsetErista; + case BootImageUpdateType::Mariko: + return BctPubkOffsetMariko; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } public: Result UpdateEks(void *dst_bct, void *eks_work_buffer); Result UpdateEksManually(void *dst_bct, const void *src_eks); Result PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which); + + Result DetectCustomPublicKey(bool *out, void *work_buffer, BootImageUpdateType boot_image_update_type); }; class Boot1Accessor : public PartitionAccessor {