/* * Copyright (c) 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 . */ #include #include "secmon_boot.hpp" namespace ams::secmon::boot { namespace { constinit const u8 RsaPublicKeyExponent[] = { 0x00, 0x01, 0x00, 0x01, }; constexpr inline u8 TailMagic = 0xBC; bool VerifyRsaPssSha256(const u8 *sig, const void *msg, size_t msg_size) { /* Define constants. */ constexpr int EmBits = 2047; constexpr int EmLen = util::DivideUp(EmBits, BITSIZEOF(u8)); constexpr int SaltLen = 0x20; constexpr int HashLen = se::Sha256HashSize; /* Define a work buffer. */ u8 work[EmLen]; ON_SCOPE_EXIT { util::ClearMemory(work, sizeof(work)); }; /* Calculate the message hash, first flushing cache to ensure SE sees correct data. */ se::Sha256Hash msg_hash; hw::FlushDataCache(msg, msg_size); hw::DataSynchronizationBarrierInnerShareable(); se::CalculateSha256(std::addressof(msg_hash), msg, msg_size); /* Verify the tail magic. */ bool is_valid = sig[EmLen - 1] == TailMagic; /* Determine extents of masked db and h. */ const u8 *masked_db = std::addressof(sig[0]); const u8 *h = std::addressof(sig[EmLen - HashLen - 1]); /* Verify the extra bits are zero. */ is_valid &= (masked_db[0] >> (BITSIZEOF(u8) - (BITSIZEOF(u8) * EmLen - EmBits))) == 0; /* Calculate the db mask. */ { constexpr int MaskLen = EmLen - HashLen - 1; constexpr int HashIters = util::DivideUp(MaskLen, HashLen); u8 mgf1_buf[sizeof(u32) + HashLen]; std::memcpy(std::addressof(mgf1_buf[0]), h, HashLen); std::memset(std::addressof(mgf1_buf[HashLen]), 0, sizeof(u32)); for (int i = 0; i < HashIters; ++i) { /* Set the counter for this iteration. */ mgf1_buf[sizeof(mgf1_buf) - 1] = i; /* Calculate the sha256 to the appropriate place in the work buffer. */ auto *mgf1_dst = reinterpret_cast(std::addressof(work[HashLen * i])); hw::FlushDataCache(mgf1_buf, sizeof(mgf1_buf)); hw::DataSynchronizationBarrierInnerShareable(); se::CalculateSha256(mgf1_dst, mgf1_buf, sizeof(mgf1_buf)); } } /* Decrypt masked db using the mask we just generated. */ for (int i = 0; i < EmLen - HashLen - 1; ++i) { work[i] ^= masked_db[i]; } /* Mask out the top bits. */ u8 *db = work; db[0] &= 0xFF >> (BITSIZEOF(u8) * EmLen - EmBits); /* Verify that DB is of the form 0000...0001 */ constexpr int DbLen = EmLen - HashLen - 1; int salt_ofs = 0; { int looking_for_one = 1; int invalid_db_padding = 0; int is_zero; int is_one; for (size_t i = 0; i < DbLen; /* ... */) { is_zero = (db[i] == 0); is_one = (db[i] == 1); salt_ofs += (looking_for_one & is_one) * (static_cast(++i)); looking_for_one &= ~is_one; invalid_db_padding |= (looking_for_one & ~is_zero); } is_valid &= (invalid_db_padding == 0); } /* Verify salt. */ is_valid &= (DbLen - salt_ofs) == SaltLen; /* Setup the message to verify. */ const u8 *salt = std::addressof(db[DbLen - SaltLen]); u8 verif_msg[8 + HashLen + SaltLen]; ON_SCOPE_EXIT { util::ClearMemory(verif_msg, sizeof(verif_msg)); }; util::ClearMemory(std::addressof(verif_msg[0]), 8); std::memcpy(std::addressof(verif_msg[8]), std::addressof(msg_hash), HashLen); std::memcpy(std::addressof(verif_msg[8 + HashLen]), salt, SaltLen); /* Verify the final hash. */ return VerifyHash(h, reinterpret_cast(std::addressof(verif_msg[0])), sizeof(verif_msg)); } bool VerifyRsaPssSha256(int slot, void *sig, size_t sig_size, const void *msg, size_t msg_size) { /* Exponentiate the signature, using the signature as the destination buffer. */ se::ModularExponentiate(sig, sig_size, slot, sig, sig_size); /* Verify the pss padding. */ return VerifyRsaPssSha256(static_cast(sig), msg, msg_size); } } bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size) { /* Load the public key into a temporary keyslot. */ const int slot = pkg1::RsaKeySlot_Temporary; se::SetRsaKey(slot, mod, mod_size, RsaPublicKeyExponent, util::size(RsaPublicKeyExponent)); return VerifyRsaPssSha256(slot, sig, sig_size, msg, msg_size); } bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size) { /* Zero-sized messages are always valid. */ if (msg_size == 0) { return true; } /* Ensure that the SE sees correct data for the message. */ hw::FlushDataCache(reinterpret_cast(msg), msg_size); hw::DataSynchronizationBarrierInnerShareable(); /* Calculate the hash. */ se::Sha256Hash calc_hash; se::CalculateSha256(std::addressof(calc_hash), reinterpret_cast(msg), msg_size); /* Verify the result. */ return crypto::IsSameBytes(std::addressof(calc_hash), hash, sizeof(calc_hash)); } }