From 4b0f8d9f853499c6d7604168d02baa7205dda460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 15 May 2018 18:01:11 +0200 Subject: [PATCH] ES: Implement VerifySign This implements ES_VerifySign which is notably used by the system menu when importing saves. Now *all* ES commands that are actually used by titles are implemented. --- Source/Core/Core/IOS/ES/ES.cpp | 3 +- Source/Core/Core/IOS/ES/ES.h | 4 ++ Source/Core/Core/IOS/ES/Identity.cpp | 99 +++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index e75bdfb77d..227f6d78fd 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -524,6 +524,8 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request) return GetDeviceCertificate(request); case IOCTL_ES_SIGN: return Sign(request); + case IOCTL_ES_VERIFYSIGN: + return VerifySign(request); case IOCTL_ES_GETBOOT2VERSION: return GetBoot2Version(request); @@ -539,7 +541,6 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request) case IOCTL_ES_DELETE_STREAM_KEY: return DeleteStreamKey(request); - case IOCTL_ES_VERIFYSIGN: case IOCTL_ES_UNKNOWN_41: case IOCTL_ES_UNKNOWN_42: PanicAlert("IOS-ES: Unimplemented ioctlv 0x%x (%zu in vectors, %zu io vectors)", diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index dd1050c6e6..174d3836e7 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -132,6 +132,9 @@ public: ReturnCode GetDeviceId(u32* device_id) const; ReturnCode GetTitleId(u64* device_id) const; + ReturnCode VerifySign(const std::vector& hash, const std::vector& ecc_signature, + const std::vector& certs); + // Views ReturnCode GetV0TicketFromView(const u8* ticket_view, u8* ticket) const; ReturnCode GetTicketFromView(const u8* ticket_view, u8* ticket, u32* ticket_size) const; @@ -243,6 +246,7 @@ private: IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request); IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request); IPCCommandResult Sign(const IOCtlVRequest& request); + IPCCommandResult VerifySign(const IOCtlVRequest& request); IPCCommandResult Encrypt(u32 uid, const IOCtlVRequest& request); IPCCommandResult Decrypt(u32 uid, const IOCtlVRequest& request); diff --git a/Source/Core/Core/IOS/ES/Identity.cpp b/Source/Core/Core/IOS/ES/Identity.cpp index 134ccdc60b..87ff012cb6 100644 --- a/Source/Core/Core/IOS/ES/Identity.cpp +++ b/Source/Core/Core/IOS/ES/Identity.cpp @@ -4,10 +4,14 @@ #include "Core/IOS/ES/ES.h" -#include #include +#include + #include "Common/Logging/Log.h" +#include "Common/ScopeGuard.h" +#include "Common/StringUtil.h" +#include "Core/ConfigManager.h" #include "Core/HW/Memmap.h" #include "Core/IOS/ES/Formats.h" #include "Core/IOS/Uids.h" @@ -114,6 +118,99 @@ IPCCommandResult ES::Sign(const IOCtlVRequest& request) m_ios.GetIOSC().Sign(sig_out, ap_cert_out, m_title_context.tmd.GetTitleId(), data, data_size); return GetDefaultReply(IPC_SUCCESS); } + +ReturnCode ES::VerifySign(const std::vector& hash, const std::vector& ecc_signature, + const std::vector& certs_bytes) +{ + if (!SConfig::GetInstance().m_enable_signature_checks) + { + WARN_LOG(IOS_ES, "VerifySign: signature checks are disabled. Skipping."); + return IPC_SUCCESS; + } + + const std::map certs = IOS::ES::ParseCertChain(certs_bytes); + if (certs.empty()) + return ES_EINVAL; + + const auto ap_iterator = std::find_if(certs.begin(), certs.end(), [](const auto& entry) { + return entry.first.length() > 2 && entry.first.compare(0, 2, "AP") == 0; + }); + if (ap_iterator == certs.end()) + return ES_UNKNOWN_ISSUER; + const IOS::ES::CertReader& ap = ap_iterator->second; + + const auto ap_issuers = SplitString(ap.GetIssuer(), '-'); + const auto ng_iterator = ap_issuers.size() > 1 ? certs.find(*ap_issuers.rbegin()) : certs.end(); + if (ng_iterator == certs.end()) + return ES_UNKNOWN_ISSUER; + const IOS::ES::CertReader& ng = ng_iterator->second; + + IOSC& iosc = m_ios.GetIOSC(); + IOSC::Handle ng_cert; + ReturnCode ret = iosc.CreateObject(&ng_cert, IOSC::TYPE_PUBLIC_KEY, IOSC::SUBTYPE_ECC233, PID_ES); + if (ret != IPC_SUCCESS) + return ret; + Common::ScopeGuard handle_guard{[&] { iosc.DeleteObject(ng_cert, PID_ES); }}; + + ret = VerifyContainer(VerifyContainerType::Device, VerifyMode::DoNotUpdateCertStore, ng, + certs_bytes, ng_cert); + if (ret != IPC_SUCCESS) + { + ERROR_LOG(IOS_ES, "VerifySign: VerifyContainer(ng) failed with error %d", ret); + return ret; + } + + ret = iosc.VerifyPublicKeySign(ap.GetSha1(), ng_cert, ap.GetSignatureData(), PID_ES); + if (ret != IPC_SUCCESS) + { + ERROR_LOG(IOS_ES, "VerifySign: IOSC_VerifyPublicKeySign(ap) failed with error %d", ret); + return ret; + } + + IOSC::Handle ap_cert; + ret = iosc.CreateObject(&ap_cert, IOSC::TYPE_PUBLIC_KEY, IOSC::SUBTYPE_ECC233, PID_ES); + if (ret != IPC_SUCCESS) + return ret; + Common::ScopeGuard handle2_guard{[&] { iosc.DeleteObject(ap_cert, PID_ES); }}; + + ret = iosc.ImportPublicKey(ap_cert, ap.GetPublicKey().data(), nullptr, PID_ES); + if (ret != IPC_SUCCESS) + { + ERROR_LOG(IOS_ES, "VerifySign: IOSC_ImportPublicKey(ap) failed with error %d", ret); + return ret; + } + + std::array sha1; + mbedtls_sha1(hash.data(), hash.size(), sha1.data()); + ret = iosc.VerifyPublicKeySign(sha1, ap_cert, ecc_signature, PID_ES); + if (ret != IPC_SUCCESS) + { + ERROR_LOG(IOS_ES, "VerifySign: IOSC_VerifyPublicKeySign(data) failed with error %d", ret); + return ret; + } + + INFO_LOG(IOS_ES, "VerifySign: all checks passed"); + return IPC_SUCCESS; +} + +IPCCommandResult ES::VerifySign(const IOCtlVRequest& request) +{ + if (!request.HasNumberOfValidVectors(3, 0)) + return GetDefaultReply(ES_EINVAL); + if (request.in_vectors[1].size != sizeof(IOS::ECCSignature)) + return GetDefaultReply(ES_EINVAL); + + std::vector hash(request.in_vectors[0].size); + Memory::CopyFromEmu(hash.data(), request.in_vectors[0].address, hash.size()); + + std::vector ecc_signature(request.in_vectors[1].size); + Memory::CopyFromEmu(ecc_signature.data(), request.in_vectors[1].address, ecc_signature.size()); + + std::vector certs(request.in_vectors[2].size); + Memory::CopyFromEmu(certs.data(), request.in_vectors[2].address, certs.size()); + + return GetDefaultReply(VerifySign(hash, ecc_signature, certs)); +} } // namespace Device } // namespace HLE } // namespace IOS