From b86f1ea7b3496a2e67659940ec7925d06a68d87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 14 May 2018 22:59:34 +0200 Subject: [PATCH 1/3] ES / IOSC: Add support for ECC certificates --- Source/Core/Core/IOS/ES/Formats.cpp | 102 ++++++++++++++++------------ Source/Core/Core/IOS/IOSC.h | 39 +++++++---- 2 files changed, 84 insertions(+), 57 deletions(-) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 6039cacb8c..2af15c0c70 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -130,7 +130,7 @@ std::array SignedBlobReader::GetSha1() const bool SignedBlobReader::IsSignatureValid() const { // Too small for the certificate type. - if (m_bytes.size() < sizeof(Cert::type)) + if (m_bytes.size() < sizeof(SignatureType)) return false; // Too small to contain the whole signature data. @@ -146,20 +146,23 @@ SignatureType SignedBlobReader::GetSignatureType() const return static_cast(Common::swap32(m_bytes.data())); } +template +static std::vector DetailGetSignatureData(It begin) +{ + const auto signature_begin = begin + offsetof(T, sig); + return std::vector(signature_begin, signature_begin + sizeof(T::sig)); +} + std::vector SignedBlobReader::GetSignatureData() const { switch (GetSignatureType()) { case SignatureType::RSA4096: - { - const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA4096, sig); - return std::vector(signature_begin, signature_begin + sizeof(SignatureRSA4096::sig)); - } + return DetailGetSignatureData(m_bytes.cbegin()); case SignatureType::RSA2048: - { - const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA2048, sig); - return std::vector(signature_begin, signature_begin + sizeof(SignatureRSA2048::sig)); - } + return DetailGetSignatureData(m_bytes.cbegin()); + case SignatureType::ECC: + return DetailGetSignatureData(m_bytes.cbegin()); default: return {}; } @@ -173,27 +176,30 @@ size_t SignedBlobReader::GetSignatureSize() const return sizeof(SignatureRSA4096); case SignatureType::RSA2048: return sizeof(SignatureRSA2048); + case SignatureType::ECC: + return sizeof(SignatureECC); default: return 0; } } +template +static std::string DetailGetIssuer(const u8* bytes) +{ + const char* issuer = reinterpret_cast(bytes + offsetof(T, issuer)); + return {issuer, strnlen(issuer, sizeof(T::issuer))}; +} + std::string SignedBlobReader::GetIssuer() const { switch (GetSignatureType()) { case SignatureType::RSA4096: - { - const char* issuer = - reinterpret_cast(m_bytes.data() + offsetof(SignatureRSA4096, issuer)); - return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA4096::issuer))); - } + return DetailGetIssuer(m_bytes.data()); case SignatureType::RSA2048: - { - const char* issuer = - reinterpret_cast(m_bytes.data() + offsetof(SignatureRSA2048, issuer)); - return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA2048::issuer))); - } + return DetailGetIssuer(m_bytes.data()); + case SignatureType::ECC: + return DetailGetIssuer(m_bytes.data()); default: return ""; } @@ -677,24 +683,22 @@ CertReader::CertReader(std::vector&& bytes) : SignedBlobReader(std::move(byt if (!IsSignatureValid()) return; - switch (GetSignatureType()) - { - case SignatureType::RSA4096: - if (m_bytes.size() < sizeof(CertRSA4096)) - return; - m_bytes.resize(sizeof(CertRSA4096)); - break; + static constexpr std::array, 4> types{{ + {SignatureType::RSA4096, PublicKeyType::RSA2048, sizeof(CertRSA4096RSA2048)}, + {SignatureType::RSA2048, PublicKeyType::RSA2048, sizeof(CertRSA2048RSA2048)}, + {SignatureType::RSA2048, PublicKeyType::ECC, sizeof(CertRSA2048ECC)}, + {SignatureType::ECC, PublicKeyType::ECC, sizeof(CertECC)}, + }}; - case SignatureType::RSA2048: - if (m_bytes.size() < sizeof(CertRSA2048)) - return; - m_bytes.resize(sizeof(CertRSA2048)); - break; + const auto info = std::find_if(types.cbegin(), types.cend(), [this](const auto& entry) { + return m_bytes.size() >= std::get<2>(entry) && std::get<0>(entry) == GetSignatureType() && + std::get<1>(entry) == GetPublicKeyType(); + }); - default: + if (info == types.cend()) return; - } + m_bytes.resize(std::get<2>(*info)); m_is_valid = true; } @@ -722,20 +726,28 @@ PublicKeyType CertReader::GetPublicKeyType() const return static_cast(Common::swap32(m_bytes.data() + offset)); } +template +std::vector DetailGetPublicKey(It begin, size_t extra_data = 0) +{ + const auto key_begin = begin + offsetof(T, public_key); + return {key_begin, key_begin + sizeof(T::public_key) + extra_data}; +} + std::vector CertReader::GetPublicKey() const { - static const std::map> type_to_key_info = {{ - {SignatureType::RSA4096, - {offsetof(CertRSA4096, public_key), - sizeof(CertRSA4096::public_key) + sizeof(CertRSA4096::exponent)}}, - {SignatureType::RSA2048, - {offsetof(CertRSA2048, public_key), - sizeof(CertRSA2048::public_key) + sizeof(CertRSA2048::exponent)}}, - }}; - - const auto info = type_to_key_info.at(GetSignatureType()); - const auto key_begin = m_bytes.begin() + info.first; - return std::vector(key_begin, key_begin + info.second); + switch (GetSignatureType()) + { + case SignatureType::RSA4096: + return DetailGetPublicKey(m_bytes.begin(), 4); + case SignatureType::RSA2048: + if (GetPublicKeyType() == PublicKeyType::RSA2048) + return DetailGetPublicKey(m_bytes.begin(), 4); + return DetailGetPublicKey(m_bytes.begin()); + case SignatureType::ECC: + return DetailGetPublicKey(m_bytes.begin()); + default: + return {}; + } } std::map ParseCertChain(const std::vector& chain) diff --git a/Source/Core/Core/IOS/IOSC.h b/Source/Core/Core/IOS/IOSC.h index c397b85a5c..5207f4c9f0 100644 --- a/Source/Core/Core/IOS/IOSC.h +++ b/Source/Core/Core/IOS/IOSC.h @@ -34,6 +34,7 @@ enum class PublicKeyType : u32 { RSA4096 = 0, RSA2048 = 1, + ECC = 2, }; #pragma pack(push, 4) @@ -72,34 +73,48 @@ struct CertHeader u32 id; }; -struct CertRSA4096 +using RSA2048PublicKey = std::array; +using ECCPublicKey = std::array; + +struct CertRSA4096RSA2048 { SignatureRSA4096 signature; CertHeader header; - // The signature is RSA4096, but the key is a RSA2048 public key, - // so its size is 0x100, not 0x200, as one would expect from the name. - u8 public_key[0x100]; + RSA2048PublicKey public_key; u8 exponent[0x4]; u8 pad[0x34]; }; -static_assert(sizeof(CertRSA4096) == 0x400, "Wrong size for CertRSA4096"); +static_assert(sizeof(CertRSA4096RSA2048) == 0x400, "Wrong size for CertRSA4096RSA2048"); -struct CertRSA2048 +struct CertRSA2048RSA2048 { SignatureRSA2048 signature; CertHeader header; - u8 public_key[0x100]; + RSA2048PublicKey public_key; u8 exponent[0x4]; u8 pad[0x34]; }; -static_assert(sizeof(CertRSA2048) == 0x300, "Wrong size for CertRSA2048"); +static_assert(sizeof(CertRSA2048RSA2048) == 0x300, "Wrong size for CertRSA2048RSA2048"); -union Cert +/// Used for device certificates +struct CertRSA2048ECC { - SignatureType type; - CertRSA4096 rsa4096; - CertRSA2048 rsa2048; + SignatureRSA2048 signature; + CertHeader header; + ECCPublicKey public_key; + std::array padding; }; +static_assert(sizeof(CertRSA2048ECC) == 0x240, "Wrong size for CertRSA2048ECC"); + +/// Used for device signed certificates +struct CertECC +{ + SignatureECC signature; + CertHeader header; + ECCPublicKey public_key; + std::array padding; +}; +static_assert(sizeof(CertECC) == 0x180, "Wrong size for CertECC"); #pragma pack(pop) using ECCSignature = std::array; From cec7fded6082e17118113ab77d2d77a9abd82886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 15 May 2018 18:01:28 +0200 Subject: [PATCH 2/3] IOSC: Implement VerifyPublicKeySign for ECC --- Source/Core/Common/Crypto/ec.cpp | 6 ++- Source/Core/Common/Crypto/ec.h | 7 +++ Source/Core/Core/IOS/IOSC.cpp | 8 +++- Source/UnitTests/Common/Crypto/EcTest.cpp | 53 +++++++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/Source/Core/Common/Crypto/ec.cpp b/Source/Core/Common/Crypto/ec.cpp index 9906ccd5e9..b9a0ca6491 100644 --- a/Source/Core/Common/Crypto/ec.cpp +++ b/Source/Core/Common/Crypto/ec.cpp @@ -278,8 +278,10 @@ std::array Sign(const u8* key, const u8* hash) return signature; } -UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash) +bool VerifySignature(const u8* public_key, const u8* signature, const u8* hash) { + const u8* R = signature; + const u8* S = signature + 30; u8 Sinv[30]; bn_inv(Sinv, S, ec_N, 30); @@ -290,7 +292,7 @@ UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash) bn_mul(w1, e, Sinv, ec_N, 30); bn_mul(w2, R, Sinv, ec_N, 30); - Point r1 = w1 * ec_G + w2 * Point{Q}; + Point r1 = w1 * ec_G + w2 * Point{public_key}; auto& rx = r1.X().data; if (bn_compare(rx.data(), ec_N, 30) >= 0) bn_sub_modulus(rx.data(), ec_N, 30); diff --git a/Source/Core/Common/Crypto/ec.h b/Source/Core/Common/Crypto/ec.h index 18b0d48d98..442bbec311 100644 --- a/Source/Core/Common/Crypto/ec.h +++ b/Source/Core/Common/Crypto/ec.h @@ -13,6 +13,13 @@ namespace Common::ec /// Generate a signature using ECDSA. std::array Sign(const u8* key, const u8* hash); +/// Check a signature using ECDSA. +/// +/// @param public_key 30 byte ECC public key +/// @param signature 60 byte signature +/// @param hash Message hash +bool VerifySignature(const u8* public_key, const u8* signature, const u8* hash); + /// Compute a shared secret from a private key (30 bytes) and public key (60 bytes). std::array ComputeSharedSecret(const u8* private_key, const u8* public_key); diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp index 569568395d..7d791b3f9d 100644 --- a/Source/Core/Core/IOS/IOSC.cpp +++ b/Source/Core/Core/IOS/IOSC.cpp @@ -337,8 +337,12 @@ ReturnCode IOSC::VerifyPublicKeySign(const std::array& sha1, Handle sign return IPC_SUCCESS; } case SUBTYPE_ECC233: - ERROR_LOG(IOS, "VerifyPublicKeySign: SUBTYPE_ECC233 is unimplemented"); - // [[fallthrough]] + { + ASSERT(entry->data.size() == sizeof(CertECC::public_key)); + + const bool ok = Common::ec::VerifySignature(entry->data.data(), signature.data(), sha1.data()); + return ok ? IPC_SUCCESS : IOSC_FAIL_CHECKVALUE; + } default: return IOSC_INVALID_OBJTYPE; } diff --git a/Source/UnitTests/Common/Crypto/EcTest.cpp b/Source/UnitTests/Common/Crypto/EcTest.cpp index 3fc955da89..b7cb25c518 100644 --- a/Source/UnitTests/Common/Crypto/EcTest.cpp +++ b/Source/UnitTests/Common/Crypto/EcTest.cpp @@ -7,6 +7,7 @@ #include #include "Common/Crypto/ec.h" +#include "Core/IOS/ES/Formats.h" constexpr std::array PRIVATE_KEY{{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, @@ -43,3 +44,55 @@ TEST(ec, GenerateSharedSecret) EXPECT_EQ(Common::ec::ComputeSharedSecret(PRIVATE_KEY.data(), PUBLIC_KEY.data()), SECRET); } + +TEST(ec, SignAndVerify) +{ + static constexpr std::array HASH = {{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}}; + + auto signature = Common::ec::Sign(PRIVATE_KEY.data(), HASH.data()); + EXPECT_TRUE(Common::ec::VerifySignature(PUBLIC_KEY.data(), signature.data(), HASH.data())); + signature.fill(0xff); + EXPECT_FALSE(Common::ec::VerifySignature(PUBLIC_KEY.data(), signature.data(), HASH.data())); +} + +TEST(ec, VerifyRealWorldSignature) +{ + static constexpr std::array MS_PUBKEY = { + {0x00, 0xfd, 0x56, 0x04, 0x18, 0x2c, 0xf1, 0x75, 0x09, 0x21, 0x00, 0xc3, 0x08, 0xae, 0x48, + 0x39, 0x91, 0x1b, 0x6f, 0x9f, 0xa1, 0xd5, 0x3a, 0x95, 0xaf, 0x08, 0x33, 0x49, 0x47, 0x2b, + 0x00, 0x01, 0x71, 0x31, 0x69, 0xb5, 0x91, 0xff, 0xd3, 0x0c, 0xbf, 0x73, 0xda, 0x76, 0x64, + 0xba, 0x8d, 0x0d, 0xf9, 0x5b, 0x4d, 0x11, 0x04, 0x44, 0x64, 0x35, 0xc0, 0xed, 0xa4, 0x2f}}; + + static constexpr std::array DEVICE_CERT = { + {0x00, 0x01, 0x00, 0x02, 0x00, 0x54, 0xe3, 0x9a, 0x0f, 0xe6, 0xe1, 0x61, 0xb6, 0x2f, 0x9d, + 0x0c, 0xaa, 0x1e, 0xc5, 0x58, 0x85, 0xa1, 0xeb, 0x93, 0xa5, 0x1e, 0xf4, 0x06, 0x99, 0x77, + 0x9a, 0x46, 0x76, 0x01, 0x00, 0xb7, 0xe4, 0x72, 0x10, 0x6e, 0xa2, 0x21, 0x57, 0xe0, 0xe3, + 0xbe, 0x48, 0x9d, 0x7b, 0xa5, 0x2d, 0x46, 0x2f, 0x33, 0x93, 0xae, 0xb0, 0x4b, 0x53, 0xcb, + 0xb9, 0xef, 0x16, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6f, 0x6f, 0x74, 0x2d, 0x43, 0x41, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2d, 0x4d, 0x53, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x4e, 0x47, 0x30, 0x34, 0x65, 0x35, 0x34, 0x32, 0x31, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x1e, 0x5f, 0x58, 0x01, 0xa8, 0x1a, 0x89, 0x8d, 0x04, + 0xe4, 0x0e, 0x44, 0x6c, 0x99, 0x52, 0xef, 0xe8, 0xe9, 0x8a, 0xec, 0x2b, 0x73, 0xea, 0x13, + 0x56, 0x93, 0xf5, 0x1a, 0xd8, 0x53, 0xa8, 0xc5, 0xf2, 0x00, 0x41, 0xe9, 0x5e, 0x0a, 0x5d, + 0x0c, 0xdf, 0xf0, 0xc6, 0x96, 0x2c, 0x98, 0x96, 0xa9, 0x0f, 0xf0, 0x2e, 0x1f, 0x0d, 0x1a, + 0xcf, 0xa8, 0x35, 0x52, 0x74, 0x36, 0x13, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + const auto device_cert = IOS::ES::CertReader{{std::cbegin(DEVICE_CERT), std::cend(DEVICE_CERT)}}; + EXPECT_TRUE(Common::ec::VerifySignature(MS_PUBKEY.data(), device_cert.GetSignatureData().data(), + device_cert.GetSha1().data())); +} 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 3/3] 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