diff options
| author | Léo Lam <leo@innovatetechnologi.es> | 2018-05-20 23:11:49 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-20 23:11:49 +0200 |
| commit | 3d8e63fffd129ae2a40983d3ac82663698329e37 (patch) | |
| tree | 7c5b6df0d8672b5fc2f4b3366f1621425e83a6bd /Source/Core | |
| parent | 79edd57b96efa7a246671646bcf2d9c41e057d7c (diff) | |
| parent | 4b0f8d9f853499c6d7604168d02baa7205dda460 (diff) | |
Merge pull request #6914 from leoetlino/verifysign
ES: Implement VerifySign (last remaining unimplemented, actually used ioctlv)
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/Common/Crypto/ec.cpp | 6 | ||||
| -rw-r--r-- | Source/Core/Common/Crypto/ec.h | 7 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/ES/ES.cpp | 3 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/ES/ES.h | 4 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/ES/Formats.cpp | 102 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/ES/Identity.cpp | 99 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/IOSC.cpp | 8 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/IOSC.h | 39 |
8 files changed, 205 insertions, 63 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<u8, 60> 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<u8, 60> 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<u8, 60> ComputeSharedSecret(const u8* private_key, const u8* public_key); 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<u8>& hash, const std::vector<u8>& ecc_signature, + const std::vector<u8>& 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/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<u8, 20> 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<SignatureType>(Common::swap32(m_bytes.data())); } +template <typename T, typename It> +static std::vector<u8> DetailGetSignatureData(It begin) +{ + const auto signature_begin = begin + offsetof(T, sig); + return std::vector<u8>(signature_begin, signature_begin + sizeof(T::sig)); +} + std::vector<u8> SignedBlobReader::GetSignatureData() const { switch (GetSignatureType()) { case SignatureType::RSA4096: - { - const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA4096, sig); - return std::vector<u8>(signature_begin, signature_begin + sizeof(SignatureRSA4096::sig)); - } + return DetailGetSignatureData<SignatureRSA4096>(m_bytes.cbegin()); case SignatureType::RSA2048: - { - const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA2048, sig); - return std::vector<u8>(signature_begin, signature_begin + sizeof(SignatureRSA2048::sig)); - } + return DetailGetSignatureData<SignatureRSA2048>(m_bytes.cbegin()); + case SignatureType::ECC: + return DetailGetSignatureData<SignatureECC>(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 <typename T> +static std::string DetailGetIssuer(const u8* bytes) +{ + const char* issuer = reinterpret_cast<const char*>(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<const char*>(m_bytes.data() + offsetof(SignatureRSA4096, issuer)); - return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA4096::issuer))); - } + return DetailGetIssuer<SignatureRSA4096>(m_bytes.data()); case SignatureType::RSA2048: - { - const char* issuer = - reinterpret_cast<const char*>(m_bytes.data() + offsetof(SignatureRSA2048, issuer)); - return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA2048::issuer))); - } + return DetailGetIssuer<SignatureRSA2048>(m_bytes.data()); + case SignatureType::ECC: + return DetailGetIssuer<SignatureECC>(m_bytes.data()); default: return ""; } @@ -677,24 +683,22 @@ CertReader::CertReader(std::vector<u8>&& 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<std::tuple<SignatureType, PublicKeyType, size_t>, 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<PublicKeyType>(Common::swap32(m_bytes.data() + offset)); } -std::vector<u8> CertReader::GetPublicKey() const +template <typename T, typename It> +std::vector<u8> DetailGetPublicKey(It begin, size_t extra_data = 0) { - static const std::map<SignatureType, std::pair<size_t, size_t>> 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 key_begin = begin + offsetof(T, public_key); + return {key_begin, key_begin + sizeof(T::public_key) + extra_data}; +} - const auto info = type_to_key_info.at(GetSignatureType()); - const auto key_begin = m_bytes.begin() + info.first; - return std::vector<u8>(key_begin, key_begin + info.second); +std::vector<u8> CertReader::GetPublicKey() const +{ + switch (GetSignatureType()) + { + case SignatureType::RSA4096: + return DetailGetPublicKey<CertRSA4096RSA2048>(m_bytes.begin(), 4); + case SignatureType::RSA2048: + if (GetPublicKeyType() == PublicKeyType::RSA2048) + return DetailGetPublicKey<CertRSA2048RSA2048>(m_bytes.begin(), 4); + return DetailGetPublicKey<CertRSA2048ECC>(m_bytes.begin()); + case SignatureType::ECC: + return DetailGetPublicKey<CertECC>(m_bytes.begin()); + default: + return {}; + } } std::map<std::string, CertReader> ParseCertChain(const std::vector<u8>& chain) 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 <cstring> #include <vector> +#include <mbedtls/sha1.h> + #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<u8>& hash, const std::vector<u8>& ecc_signature, + const std::vector<u8>& 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<std::string, IOS::ES::CertReader> 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<u8, 20> 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<u8> hash(request.in_vectors[0].size); + Memory::CopyFromEmu(hash.data(), request.in_vectors[0].address, hash.size()); + + std::vector<u8> ecc_signature(request.in_vectors[1].size); + Memory::CopyFromEmu(ecc_signature.data(), request.in_vectors[1].address, ecc_signature.size()); + + std::vector<u8> 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 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<u8, 20>& 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/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<u8, 0x100>; +using ECCPublicKey = std::array<u8, 60>; + +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<u8, 60> 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<u8, 60> padding; }; +static_assert(sizeof(CertECC) == 0x180, "Wrong size for CertECC"); #pragma pack(pop) using ECCSignature = std::array<u8, 60>; |
